diff options
Diffstat (limited to 'sound/soc/codecs/ak4535.c')
| -rw-r--r-- | sound/soc/codecs/ak4535.c | 223 |
1 files changed, 75 insertions, 148 deletions
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index cd88c8f32a3..30e297890fe 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -18,93 +18,53 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.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 "ak4535.h" -#define AK4535_VERSION "0.3" - /* codec private data */ struct ak4535_priv { + struct regmap *regmap; unsigned int sysclk; - enum snd_soc_control_type control_type; - void *control_data; }; /* * ak4535 register cache */ -static const u16 ak4535_reg[AK4535_CACHEREGNUM] = { - 0x0000, 0x0080, 0x0000, 0x0003, - 0x0002, 0x0000, 0x0011, 0x0001, - 0x0000, 0x0040, 0x0036, 0x0010, - 0x0000, 0x0000, 0x0057, 0x0000, +static const struct reg_default ak4535_reg_defaults[] = { + { 0, 0x00 }, + { 1, 0x80 }, + { 2, 0x00 }, + { 3, 0x03 }, + { 4, 0x02 }, + { 5, 0x00 }, + { 6, 0x11 }, + { 7, 0x01 }, + { 8, 0x00 }, + { 9, 0x40 }, + { 10, 0x36 }, + { 11, 0x10 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x57 }, }; -/* - * read ak4535 register cache - */ -static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - if (reg >= AK4535_CACHEREGNUM) - return -1; - return cache[reg]; -} - -/* - * write ak4535 register cache - */ -static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, unsigned int value) -{ - u16 *cache = codec->reg_cache; - if (reg >= AK4535_CACHEREGNUM) - return; - cache[reg] = value; -} - -/* - * write to the AK4535 register space - */ -static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +static bool ak4535_volatile(struct device *dev, unsigned int reg) { - u8 data[2]; - - /* data is - * D15..D8 AK4535 register offset - * D7...D0 register data - */ - data[0] = reg & 0xff; - data[1] = value & 0xff; - - ak4535_write_reg_cache(codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; + switch (reg) { + case AK4535_STATUS: + return true; + default: + return false; + } } -static int ak4535_sync(struct snd_soc_codec *codec) -{ - u16 *cache = codec->reg_cache; - int i, r = 0; - - for (i = 0; i < AK4535_CACHEREGNUM; i++) - r |= ak4535_write(codec, i, cache[i]); - - return r; -}; - static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"}; static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"}; static const char *ak4535_hp_out[] = {"Stereo", "Mono"}; @@ -231,7 +191,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AIN"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route ak4535_audio_map[] = { /*stereo mixer */ {"Stereo Mixer", "Playback Switch", "DAC"}, {"Stereo Mixer", "Mic Sidetone Switch", "Mic"}, @@ -288,16 +248,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Input Mixer", "Aux Capture Switch", "Aux In"}, }; -static int ak4535_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets, - ARRAY_SIZE(ak4535_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -312,10 +262,9 @@ static int ak4535_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_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec); - u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5); + u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5); int rate = params_rate(params), fs = 256; if (rate) @@ -334,7 +283,7 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream, } /* set rate */ - ak4535_write(codec, AK4535_MODE2, mode2); + snd_soc_write(codec, AK4535_MODE2, mode2); return 0; } @@ -359,47 +308,40 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai, /* use 32 fs for BCLK to save power */ mode1 |= 0x4; - ak4535_write(codec, AK4535_MODE1, mode1); + snd_soc_write(codec, AK4535_MODE1, mode1); return 0; } static int ak4535_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; + u16 mute_reg = snd_soc_read(codec, AK4535_DAC); if (!mute) - ak4535_write(codec, AK4535_DAC, mute_reg); + snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20); else - ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); + snd_soc_write(codec, AK4535_DAC, mute_reg | 0x20); return 0; } static int ak4535_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 i, mute_reg; - switch (level) { case SND_SOC_BIAS_ON: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; - ak4535_write(codec, AK4535_DAC, mute_reg); + snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0); break; case SND_SOC_BIAS_PREPARE: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; - ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); + snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0x20); break; case SND_SOC_BIAS_STANDBY: - i = ak4535_read_reg_cache(codec, AK4535_PM1); - ak4535_write(codec, AK4535_PM1, i | 0x80); - i = ak4535_read_reg_cache(codec, AK4535_PM2); - ak4535_write(codec, AK4535_PM2, i & (~0x80)); + snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0x80); + snd_soc_update_bits(codec, AK4535_PM2, 0x80, 0); break; case SND_SOC_BIAS_OFF: - i = ak4535_read_reg_cache(codec, AK4535_PM1); - ak4535_write(codec, AK4535_PM1, i & (~0x80)); + snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -407,7 +349,7 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) -static struct snd_soc_dai_ops ak4535_dai_ops = { +static const struct snd_soc_dai_ops ak4535_dai_ops = { .hw_params = ak4535_hw_params, .set_fmt = ak4535_set_dai_fmt, .digital_mute = ak4535_mute, @@ -431,7 +373,7 @@ static struct snd_soc_dai_driver ak4535_dai = { .ops = &ak4535_dai_ops, }; -static int ak4535_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int ak4535_suspend(struct snd_soc_codec *codec) { ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; @@ -439,26 +381,18 @@ static int ak4535_suspend(struct snd_soc_codec *codec, pm_message_t state) static int ak4535_resume(struct snd_soc_codec *codec) { - ak4535_sync(codec); + snd_soc_cache_sync(codec); ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } static int ak4535_probe(struct snd_soc_codec *codec) { - struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec); - - printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION); - - codec->control_data = ak4535->control_data; - /* power on device */ ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_controls(codec, ak4535_snd_controls, + snd_soc_add_codec_controls(codec, ak4535_snd_controls, ARRAY_SIZE(ak4535_snd_controls)); - ak4535_add_widgets(codec); - return 0; } @@ -469,45 +403,59 @@ static int ak4535_remove(struct snd_soc_codec *codec) return 0; } +static const struct regmap_config ak4535_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4535_STATUS, + .volatile_reg = ak4535_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ak4535_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults), +}; + static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { .probe = ak4535_probe, .remove = ak4535_remove, .suspend = ak4535_suspend, .resume = ak4535_resume, - .read = ak4535_read_reg_cache, - .write = ak4535_write, .set_bias_level = ak4535_set_bias_level, - .reg_cache_size = ARRAY_SIZE(ak4535_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = ak4535_reg, + .dapm_widgets = ak4535_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), + .dapm_routes = ak4535_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map), }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static __devinit int ak4535_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ak4535_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct ak4535_priv *ak4535; int ret; - ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL); + ak4535 = devm_kzalloc(&i2c->dev, sizeof(struct ak4535_priv), + GFP_KERNEL); if (ak4535 == NULL) return -ENOMEM; + ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap); + if (IS_ERR(ak4535->regmap)) { + ret = PTR_ERR(ak4535->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + i2c_set_clientdata(i2c, ak4535); - ak4535->control_data = i2c; - ak4535->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4535, &ak4535_dai, 1); - if (ret < 0) - kfree(ak4535); + return ret; } -static __devexit int ak4535_i2c_remove(struct i2c_client *client) +static int ak4535_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); - kfree(i2c_get_clientdata(client)); return 0; } @@ -519,36 +467,15 @@ MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); static struct i2c_driver ak4535_i2c_driver = { .driver = { - .name = "ak4535-codec", + .name = "ak4535", .owner = THIS_MODULE, }, .probe = ak4535_i2c_probe, - .remove = __devexit_p(ak4535_i2c_remove), + .remove = ak4535_i2c_remove, .id_table = ak4535_i2c_id, }; -#endif - -static int __init ak4535_modinit(void) -{ - int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&ak4535_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register AK4535 I2C driver: %d\n", - ret); - } -#endif - return ret; -} -module_init(ak4535_modinit); -static void __exit ak4535_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&ak4535_i2c_driver); -#endif -} -module_exit(ak4535_exit); +module_i2c_driver(ak4535_i2c_driver); MODULE_DESCRIPTION("Soc AK4535 driver"); MODULE_AUTHOR("Richard Purdie"); |
