diff options
Diffstat (limited to 'sound/soc/codecs/cs4270.c')
| -rw-r--r-- | sound/soc/codecs/cs4270.c | 354 |
1 files changed, 148 insertions, 206 deletions
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 6d4bdc609ac..9947a958367 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/soc.h> @@ -30,6 +29,8 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> /* * The codec isn't really big-endian or little-endian, since the I2S @@ -106,15 +107,29 @@ #define CS4270_MUTE_DAC_A 0x01 #define CS4270_MUTE_DAC_B 0x02 +/* Power-on default values for the registers + * + * This array contains the power-on default values of the registers, with the + * exception of the "CHIPID" register (01h). The lower four bits of that + * register contain the hardware revision, so it is treated as volatile. + */ +static const struct reg_default cs4270_reg_defaults[] = { + { 2, 0x00 }, + { 3, 0x30 }, + { 4, 0x00 }, + { 5, 0x60 }, + { 6, 0x20 }, + { 7, 0x00 }, + { 8, 0x00 }, +}; + static const char *supply_names[] = { "va", "vd", "vlc" }; /* Private data for the CS4270 */ struct cs4270_private { - enum snd_soc_control_type control_type; - void *control_data; - u8 reg_cache[CS4270_NUMREGS]; + struct regmap *regmap; unsigned int mclk; /* Input frequency of the MCLK pin */ unsigned int mode; /* The mode (I2S or left-justified) */ unsigned int slave_mode; @@ -124,6 +139,22 @@ struct cs4270_private { struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; +static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA", NULL, "Playback" }, + { "AOUTB", NULL, "Playback" }, +}; + /** * struct cs4270_mode_ratios - clock ratio tables * @ratio: the ratio of MCLK to the sample rate @@ -179,6 +210,20 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { /* The number of MCLK/LRCK ratios supported by the CS4270 */ #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) +static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg) +{ + return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); +} + +static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg) +{ + /* Unreadable registers are considered volatile */ + if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) + return 1; + + return reg == CS4270_CHIPID; +} + /** * cs4270_set_dai_sysclk - determine the CS4270 samples rates. * @codec_dai: the codec DAI @@ -234,7 +279,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); - int ret = 0; /* set DAI format */ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -244,7 +288,7 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, break; default: dev_err(codec->dev, "invalid dai format\n"); - ret = -EINVAL; + return -EINVAL; } /* set master/slave audio interface */ @@ -257,98 +301,8 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, break; default: /* all other modes are unsupported by the hardware */ - ret = -EINVAL; - } - - return ret; -} - -/** - * cs4270_fill_cache - pre-fill the CS4270 register cache. - * @codec: the codec for this CS4270 - * - * This function fills in the CS4270 register cache by reading the register - * values from the hardware. - * - * This CS4270 registers are cached to avoid excessive I2C I/O operations. - * After the initial read to pre-fill the cache, the CS4270 never updates - * the register values, so we won't have a cache coherency problem. - * - * We use the auto-increment feature of the CS4270 to read all registers in - * one shot. - */ -static int cs4270_fill_cache(struct snd_soc_codec *codec) -{ - u8 *cache = codec->reg_cache; - struct i2c_client *i2c_client = codec->control_data; - s32 length; - - length = i2c_smbus_read_i2c_block_data(i2c_client, - CS4270_FIRSTREG | CS4270_I2C_INCR, CS4270_NUMREGS, cache); - - if (length != CS4270_NUMREGS) { - dev_err(codec->dev, "i2c read failure, addr=0x%x\n", - i2c_client->addr); - return -EIO; - } - - return 0; -} - -/** - * cs4270_read_reg_cache - read from the CS4270 register cache. - * @codec: the codec for this CS4270 - * @reg: the register to read - * - * This function returns the value for a given register. It reads only from - * the register cache, not the hardware itself. - * - * This CS4270 registers are cached to avoid excessive I2C I/O operations. - * After the initial read to pre-fill the cache, the CS4270 never updates - * the register values, so we won't have a cache coherency problem. - */ -static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return -EIO; - - return cache[reg - CS4270_FIRSTREG]; -} - -/** - * cs4270_i2c_write - write to a CS4270 register via the I2C bus. - * @codec: the codec for this CS4270 - * @reg: the register to write - * @value: the value to write to the register - * - * This function writes the given value to the given CS4270 register, and - * also updates the register cache. - * - * Note that we don't use the hw_write function pointer of snd_soc_codec. - * That's because it's too clunky: the hw_write_t prototype does not match - * i2c_smbus_write_byte_data(), and it's just another layer of overhead. - */ -static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 *cache = codec->reg_cache; - - if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return -EIO; - - /* Only perform an I2C operation if the new value is different */ - if (cache[reg - CS4270_FIRSTREG] != value) { - struct i2c_client *client = codec->control_data; - if (i2c_smbus_write_byte_data(client, reg, value)) { - dev_err(codec->dev, "i2c write failed\n"); - return -EIO; - } - - /* We've written to the hardware, so update the cache */ - cache[reg - CS4270_FIRSTREG] = value; + dev_err(codec->dev, "Unknown master/slave configuration\n"); + return -EINVAL; } return 0; @@ -372,8 +326,7 @@ static int cs4270_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 cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); int ret; unsigned int i; @@ -485,7 +438,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute) static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); int left = !ucontrol->value.integer.value[0]; int right = !ucontrol->value.integer.value[1]; @@ -511,7 +464,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { snd_soc_get_volsw, cs4270_soc_put_mute), }; -static struct snd_soc_dai_ops cs4270_dai_ops = { +static const struct snd_soc_dai_ops cs4270_dai_ops = { .hw_params = cs4270_hw_params, .set_sysclk = cs4270_set_dai_sysclk, .set_fmt = cs4270_set_dai_fmt, @@ -522,7 +475,7 @@ static struct snd_soc_dai_driver cs4270_dai = { .name = "cs4270-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 4000, @@ -531,7 +484,7 @@ static struct snd_soc_dai_driver cs4270_dai = { }, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 4000, @@ -551,27 +504,14 @@ static struct snd_soc_dai_driver cs4270_dai = { static int cs4270_probe(struct snd_soc_codec *codec) { struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); - int i, ret, reg; - - codec->control_data = cs4270->control_data; - - /* The I2C interface is set up, so pre-fill our register cache */ - - ret = cs4270_fill_cache(codec); - if (ret < 0) { - dev_err(codec->dev, "failed to fill register cache\n"); - return ret; - } + int ret; /* Disable auto-mute. This feature appears to be buggy. In some * situations, auto-mute will not deactivate when it should, so we want * this feature disabled by default. An application (e.g. alsactl) can * re-enabled it by using the controls. */ - - reg = cs4270_read_reg_cache(codec, CS4270_MUTE); - reg &= ~CS4270_MUTE_AUTO; - ret = cs4270_i2c_write(codec, CS4270_MUTE, reg); + ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0); if (ret < 0) { dev_err(codec->dev, "i2c write failed\n"); return ret; @@ -582,42 +522,15 @@ static int cs4270_probe(struct snd_soc_codec *codec) * playback has started. An application (e.g. alsactl) can * re-enabled it by using the controls. */ - - reg = cs4270_read_reg_cache(codec, CS4270_TRANS); - reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO); - ret = cs4270_i2c_write(codec, CS4270_TRANS, reg); + ret = snd_soc_update_bits(codec, CS4270_TRANS, + CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0); if (ret < 0) { dev_err(codec->dev, "i2c write failed\n"); return ret; } - /* Add the non-DAPM controls */ - ret = snd_soc_add_controls(codec, cs4270_snd_controls, - ARRAY_SIZE(cs4270_snd_controls)); - if (ret < 0) { - dev_err(codec->dev, "failed to add controls\n"); - return ret; - } - - /* get the power supply regulators */ - for (i = 0; i < ARRAY_SIZE(supply_names); i++) - cs4270->supplies[i].supply = supply_names[i]; - - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies), - cs4270->supplies); - if (ret < 0) - return ret; - ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); - if (ret < 0) - goto error_free_regulators; - - return 0; - -error_free_regulators: - regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), - cs4270->supplies); return ret; } @@ -633,7 +546,6 @@ static int cs4270_remove(struct snd_soc_codec *codec) struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); - regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); return 0; }; @@ -649,7 +561,7 @@ static int cs4270_remove(struct snd_soc_codec *codec) * and all registers are written back to the hardware when resuming. */ -static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +static int cs4270_soc_suspend(struct snd_soc_codec *codec) { struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); int reg, ret; @@ -671,25 +583,19 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) static int cs4270_soc_resume(struct snd_soc_codec *codec) { struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *i2c_client = codec->control_data; - int reg; + int reg, ret; - regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), - cs4270->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + if (ret != 0) + return ret; /* In case the device was put to hard reset during sleep, we need to * wait 500ns here before any I2C communication. */ ndelay(500); /* first restore the entire register cache ... */ - for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) { - u8 val = snd_soc_read(codec, reg); - - if (i2c_smbus_write_byte_data(i2c_client, reg, val)) { - dev_err(codec->dev, "i2c write failed\n"); - return -EIO; - } - } + regcache_sync(cs4270->regmap); /* ... then disable the power-down bits */ reg = snd_soc_read(codec, CS4270_PWRCTL); @@ -703,20 +609,41 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec) #endif /* CONFIG_PM */ /* - * ASoC codec device structure - * - * Assign this variable to the codec_dev field of the machine driver's - * snd_soc_device structure. + * ASoC codec driver structure */ -static struct snd_soc_codec_driver soc_codec_device_cs4270 = { - .probe = cs4270_probe, - .remove = cs4270_remove, - .suspend = cs4270_soc_suspend, - .resume = cs4270_soc_resume, - .read = cs4270_read_reg_cache, - .write = cs4270_i2c_write, - .reg_cache_size = CS4270_NUMREGS, - .reg_word_size = sizeof(u8), +static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { + .probe = cs4270_probe, + .remove = cs4270_remove, + .suspend = cs4270_soc_suspend, + .resume = cs4270_soc_resume, + + .controls = cs4270_snd_controls, + .num_controls = ARRAY_SIZE(cs4270_snd_controls), + .dapm_widgets = cs4270_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), + .dapm_routes = cs4270_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), +}; + +/* + * cs4270_of_match - the device tree bindings + */ +static const struct of_device_id cs4270_of_match[] = { + { .compatible = "cirrus,cs4270", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4270_of_match); + +static const struct regmap_config cs4270_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = CS4270_LASTREG, + .reg_defaults = cs4270_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .readable_reg = cs4270_reg_is_readable, + .volatile_reg = cs4270_reg_is_volatile, }; /** @@ -730,19 +657,56 @@ static struct snd_soc_codec_driver soc_codec_device_cs4270 = { static int cs4270_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { + struct device_node *np = i2c_client->dev.of_node; struct cs4270_private *cs4270; - int ret; + unsigned int val; + int ret, i; - /* Verify that we have a CS4270 */ + cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private), + GFP_KERNEL); + if (!cs4270) { + dev_err(&i2c_client->dev, "could not allocate codec\n"); + return -ENOMEM; + } + + /* get the power supply regulators */ + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + cs4270->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + if (ret < 0) + return ret; - ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); + /* See if we have a way to bring the codec out of reset */ + if (np) { + enum of_gpio_flags flags; + int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); + + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(&i2c_client->dev, gpio, + flags & OF_GPIO_ACTIVE_LOW ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, + "cs4270 reset"); + if (ret < 0) + return ret; + } + } + + cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap); + if (IS_ERR(cs4270->regmap)) + return PTR_ERR(cs4270->regmap); + + /* Verify that we have a CS4270 */ + ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val); if (ret < 0) { dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n", i2c_client->addr); return ret; } /* The top four bits of the chip ID should be 1100. */ - if ((ret & 0xF0) != 0xC0) { + if ((val & 0xF0) != 0xC0) { dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n", i2c_client->addr); return -ENODEV; @@ -750,22 +714,12 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client, dev_info(&i2c_client->dev, "found device at i2c address %X\n", i2c_client->addr); - dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF); - - cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL); - if (!cs4270) { - dev_err(&i2c_client->dev, "could not allocate codec\n"); - return -ENOMEM; - } + dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF); i2c_set_clientdata(i2c_client, cs4270); - cs4270->control_data = i2c_client; - cs4270->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_device_cs4270, &cs4270_dai, 1); - if (ret < 0) - kfree(cs4270); return ret; } @@ -778,14 +732,13 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client, static int cs4270_i2c_remove(struct i2c_client *i2c_client) { snd_soc_unregister_codec(&i2c_client->dev); - kfree(i2c_get_clientdata(i2c_client)); return 0; } /* * cs4270_id - I2C device IDs supported by this driver */ -static struct i2c_device_id cs4270_id[] = { +static const struct i2c_device_id cs4270_id[] = { {"cs4270", 0}, {} }; @@ -799,27 +752,16 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id); */ static struct i2c_driver cs4270_i2c_driver = { .driver = { - .name = "cs4270-codec", + .name = "cs4270", .owner = THIS_MODULE, + .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, .probe = cs4270_i2c_probe, .remove = cs4270_i2c_remove, }; -static int __init cs4270_init(void) -{ - pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n"); - - return i2c_add_driver(&cs4270_i2c_driver); -} -module_init(cs4270_init); - -static void __exit cs4270_exit(void) -{ - i2c_del_driver(&cs4270_i2c_driver); -} -module_exit(cs4270_exit); +module_i2c_driver(cs4270_i2c_driver); MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver"); |
