diff options
Diffstat (limited to 'sound/soc/codecs/alc5623.c')
| -rw-r--r-- | sound/soc/codecs/alc5623.c | 214 |
1 files changed, 108 insertions, 106 deletions
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 4f377c9e868..9d0755aa1d1 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -21,8 +21,9 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> -#include <linux/platform_device.h> +#include <linux/of.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -39,26 +40,13 @@ MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)"); /* codec private data */ struct alc5623_priv { - enum snd_soc_control_type control_type; - void *control_data; - struct mutex mutex; + struct regmap *regmap; u8 id; unsigned int sysclk; - u16 reg_cache[ALC5623_VENDOR_ID2+2]; unsigned int add_ctrl; unsigned int jack_det_ctrl; }; -static void alc5623_fill_cache(struct snd_soc_codec *codec) -{ - int i, step = codec->driver->reg_cache_step; - u16 *cache = codec->reg_cache; - - /* not really efficient ... */ - for (i = 0 ; i < codec->driver->reg_cache_size ; i += step) - cache[i] = codec->hw_read(codec, i); -} - static inline int alc5623_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, ALC5623_RESET, 0); @@ -100,7 +88,7 @@ static const unsigned int boost_tlv[] = { }; static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); -static const struct snd_kcontrol_new rt5621_vol_snd_controls[] = { +static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), SOC_DOUBLE("Speaker Playback Switch", @@ -111,7 +99,7 @@ static const struct snd_kcontrol_new rt5621_vol_snd_controls[] = { ALC5623_HP_OUT_VOL, 15, 7, 1, 1), }; -static const struct snd_kcontrol_new rt5622_vol_snd_controls[] = { +static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), SOC_DOUBLE("Speaker Playback Switch", @@ -229,32 +217,37 @@ static const char *alc5623_aux_out_input_sel[] = { "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; /* auxout output mux */ -static const struct soc_enum alc5623_aux_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 6, 4, alc5623_aux_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 6, + alc5623_aux_out_input_sel); static const struct snd_kcontrol_new alc5623_auxout_mux_controls = SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum); /* speaker output mux */ -static const struct soc_enum alc5623_spkout_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 10, 4, alc5623_spkout_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 10, + alc5623_spkout_input_sel); static const struct snd_kcontrol_new alc5623_spkout_mux_controls = SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum); /* headphone left output mux */ -static const struct soc_enum alc5623_hpl_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 9, + alc5623_hpl_out_input_sel); static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls = SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum); /* headphone right output mux */ -static const struct soc_enum alc5623_hpr_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 8, 2, alc5623_hpr_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 8, + alc5623_hpr_out_input_sel); static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls = SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum); /* speaker output N select */ -static const struct soc_enum alc5623_spk_n_sour_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum, + ALC5623_OUTPUT_MIXER_CTRL, 14, + alc5623_spk_n_sour_sel); static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls = SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum); @@ -339,8 +332,9 @@ SND_SOC_DAPM_VMID("Vmid"), }; static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"}; -static const struct soc_enum alc5623_amp_enum = - SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 13, 2, alc5623_amp_names); +static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum, + ALC5623_OUTPUT_MIXER_CTRL, 13, + alc5623_amp_names); static const struct snd_kcontrol_new alc5623_amp_mux_controls = SOC_DAPM_ENUM("Route", alc5623_amp_enum); @@ -481,7 +475,7 @@ struct _pll_div { }; /* Note : pll code from original alc5623 driver. Not sure of how good it is */ -/* usefull only for master mode */ +/* useful only for master mode */ static const struct _pll_div codec_master_pll_div[] = { { 2048000, 8192000, 0x0ea0}, @@ -706,8 +700,7 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, static int alc5623_pcm_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 alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); int coeff, rate; u16 iface; @@ -716,17 +709,17 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream, iface &= ~ALC5623_DAI_I2S_DL_MASK; /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: iface |= ALC5623_DAI_I2S_DL_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: iface |= ALC5623_DAI_I2S_DL_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: iface |= ALC5623_DAI_I2S_DL_24; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: iface |= ALC5623_DAI_I2S_DL_32; break; default: @@ -839,7 +832,7 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec, | SNDRV_PCM_FMTBIT_S24_LE \ | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops alc5623_dai_ops = { +static const struct snd_soc_dai_ops alc5623_dai_ops = { .hw_params = alc5623_pcm_hw_params, .digital_mute = alc5623_mute, .set_fmt = alc5623_set_dai_fmt, @@ -869,20 +862,30 @@ static struct snd_soc_dai_driver alc5623_dai = { .ops = &alc5623_dai_ops, }; -static int alc5623_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +static int alc5623_suspend(struct snd_soc_codec *codec) { + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); + regcache_cache_only(alc5623->regmap, true); + return 0; } static int alc5623_resume(struct snd_soc_codec *codec) { - int i, step = codec->driver->reg_cache_step; - u16 *cache = codec->reg_cache; + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + int ret; /* Sync reg_cache with the hardware */ - for (i = 2 ; i < codec->driver->reg_cache_size ; i += step) - snd_soc_write(codec, i, cache[i]); + regcache_cache_only(alc5623->regmap, false); + ret = regcache_sync(alc5623->regmap); + if (ret != 0) { + dev_err(codec->dev, "Failed to sync register cache: %d\n", + ret); + regcache_cache_only(alc5623->regmap, true); + return ret; + } alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -900,16 +903,8 @@ static int alc5623_probe(struct snd_soc_codec *codec) { struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5623->control_type); - if (ret < 0) { - dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } alc5623_reset(codec); - alc5623_fill_cache(codec); /* power on device */ alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -926,22 +921,22 @@ static int alc5623_probe(struct snd_soc_codec *codec) switch (alc5623->id) { case 0x21: - snd_soc_add_controls(codec, rt5621_vol_snd_controls, - ARRAY_SIZE(rt5621_vol_snd_controls)); + snd_soc_add_codec_controls(codec, alc5621_vol_snd_controls, + ARRAY_SIZE(alc5621_vol_snd_controls)); break; case 0x22: - snd_soc_add_controls(codec, rt5622_vol_snd_controls, - ARRAY_SIZE(rt5622_vol_snd_controls)); + snd_soc_add_codec_controls(codec, alc5622_vol_snd_controls, + ARRAY_SIZE(alc5622_vol_snd_controls)); break; case 0x23: - snd_soc_add_controls(codec, alc5623_vol_snd_controls, + snd_soc_add_codec_controls(codec, alc5623_vol_snd_controls, ARRAY_SIZE(alc5623_vol_snd_controls)); break; default: return -EINVAL; } - snd_soc_add_controls(codec, alc5623_snd_controls, + snd_soc_add_codec_controls(codec, alc5623_snd_controls, ARRAY_SIZE(alc5623_snd_controls)); snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets, @@ -966,7 +961,7 @@ static int alc5623_probe(struct snd_soc_codec *codec) return -EINVAL; } - return ret; + return 0; } /* power down chip */ @@ -982,9 +977,15 @@ static struct snd_soc_codec_driver soc_codec_device_alc5623 = { .suspend = alc5623_suspend, .resume = alc5623_resume, .set_bias_level = alc5623_set_bias_level, - .reg_cache_size = ALC5623_VENDOR_ID2+2, - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, +}; + +static const struct regmap_config alc5623_regmap = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 2, + + .max_register = ALC5623_VENDOR_ID2, + .cache_type = REGCACHE_RBTREE, }; /* @@ -994,25 +995,40 @@ static struct snd_soc_codec_driver soc_codec_device_alc5623 = { * high = 0x1b */ static int alc5623_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct alc5623_platform_data *pdata; struct alc5623_priv *alc5623; - int ret, vid1, vid2; + struct device_node *np; + unsigned int vid1, vid2; + int ret; + u32 val32; - vid1 = i2c_smbus_read_word_data(client, ALC5623_VENDOR_ID1); - if (vid1 < 0) { - dev_err(&client->dev, "failed to read I2C\n"); - return -EIO; + alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), + GFP_KERNEL); + if (alc5623 == NULL) + return -ENOMEM; + + alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap); + if (IS_ERR(alc5623->regmap)) { + ret = PTR_ERR(alc5623->regmap); + dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret); + return ret; } - vid1 = ((vid1 & 0xff) << 8) | (vid1 >> 8); - vid2 = i2c_smbus_read_byte_data(client, ALC5623_VENDOR_ID2); - if (vid2 < 0) { - dev_err(&client->dev, "failed to read I2C\n"); - return -EIO; + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret); + return ret; } + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret); + return ret; + } + vid2 >>= 8; + if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { dev_err(&client->dev, "unknown or wrong codec\n"); dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n", @@ -1023,14 +1039,20 @@ static int alc5623_i2c_probe(struct i2c_client *client, dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2); - alc5623 = kzalloc(sizeof(struct alc5623_priv), GFP_KERNEL); - if (alc5623 == NULL) - return -ENOMEM; - pdata = client->dev.platform_data; if (pdata) { alc5623->add_ctrl = pdata->add_ctrl; alc5623->jack_det_ctrl = pdata->jack_det_ctrl; + } else { + if (client->dev.of_node) { + np = client->dev.of_node; + ret = of_property_read_u32(np, "add-ctrl", &val32); + if (!ret) + alc5623->add_ctrl = val32; + ret = of_property_read_u32(np, "jack-det-ctrl", &val32); + if (!ret) + alc5623->jack_det_ctrl = val32; + } } alc5623->id = vid2; @@ -1045,31 +1067,22 @@ static int alc5623_i2c_probe(struct i2c_client *client, alc5623_dai.name = "alc5623-hifi"; break; default: - kfree(alc5623); return -EINVAL; } i2c_set_clientdata(client, alc5623); - alc5623->control_data = client; - alc5623->control_type = SND_SOC_I2C; - mutex_init(&alc5623->mutex); ret = snd_soc_register_codec(&client->dev, &soc_codec_device_alc5623, &alc5623_dai, 1); - if (ret != 0) { + if (ret != 0) dev_err(&client->dev, "Failed to register codec: %d\n", ret); - kfree(alc5623); - } return ret; } static int alc5623_i2c_remove(struct i2c_client *client) { - struct alc5623_priv *alc5623 = i2c_get_clientdata(client); - snd_soc_unregister_codec(&client->dev); - kfree(alc5623); return 0; } @@ -1081,36 +1094,25 @@ static const struct i2c_device_id alc5623_i2c_table[] = { }; MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); +static const struct of_device_id alc5623_of_match[] = { + { .compatible = "realtek,alc5623", }, + { } +}; +MODULE_DEVICE_TABLE(of, alc5623_of_match); + /* i2c codec control layer */ static struct i2c_driver alc5623_i2c_driver = { .driver = { .name = "alc562x-codec", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(alc5623_of_match), }, .probe = alc5623_i2c_probe, - .remove = __devexit_p(alc5623_i2c_remove), + .remove = alc5623_i2c_remove, .id_table = alc5623_i2c_table, }; -static int __init alc5623_modinit(void) -{ - int ret; - - ret = i2c_add_driver(&alc5623_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "%s: can't add i2c driver", __func__); - return ret; - } - - return ret; -} -module_init(alc5623_modinit); - -static void __exit alc5623_modexit(void) -{ - i2c_del_driver(&alc5623_i2c_driver); -} -module_exit(alc5623_modexit); +module_i2c_driver(alc5623_i2c_driver); MODULE_DESCRIPTION("ASoC alc5621/2/3 driver"); MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); |
