ASoC: improve I2C initialization code in CS4270 driver
Further improvements in the I2C initialization sequence of the CS4270 driver. All ASoC initialization is now done in the I2C probe function. Signed-off-by: Timur Tabi <timur@freescale.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
parent
070504ade7
commit
0db4d07052
|
@ -31,12 +31,6 @@
|
|||
|
||||
#include "cs4270.h"
|
||||
|
||||
/* Private data for the CS4270 */
|
||||
struct cs4270_private {
|
||||
unsigned int mclk; /* Input frequency of the MCLK pin */
|
||||
unsigned int mode; /* The mode (I2S or left-justified) */
|
||||
};
|
||||
|
||||
/*
|
||||
* The codec isn't really big-endian or little-endian, since the I2S
|
||||
* interface requires data to be sent serially with the MSbit first.
|
||||
|
@ -109,6 +103,14 @@ struct cs4270_private {
|
|||
#define CS4270_MUTE_DAC_A 0x01
|
||||
#define CS4270_MUTE_DAC_B 0x02
|
||||
|
||||
/* Private data for the CS4270 */
|
||||
struct cs4270_private {
|
||||
struct snd_soc_codec codec;
|
||||
u8 reg_cache[CS4270_NUMREGS];
|
||||
unsigned int mclk; /* Input frequency of the MCLK pin */
|
||||
unsigned int mode; /* The mode (I2S or left-justified) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock Ratio Selection for Master Mode with I2C enabled
|
||||
*
|
||||
|
@ -504,112 +506,8 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
|
|||
*/
|
||||
static struct snd_soc_device *cs4270_socdev;
|
||||
|
||||
/*
|
||||
* Initialize the I2C interface of the CS4270
|
||||
*
|
||||
* This function is called for whenever the I2C subsystem finds a device
|
||||
* at a particular address.
|
||||
*
|
||||
* Note: snd_soc_new_pcms() must be called before this function can be called,
|
||||
* because of snd_ctl_add().
|
||||
*/
|
||||
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_device *socdev = cs4270_socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
/* Probing all possible addresses has one drawback: if there are
|
||||
multiple CS4270s on the bus, then you cannot specify which
|
||||
socdev is matched with which CS4270. For now, we just reject
|
||||
this I2C device if the socdev already has one attached. */
|
||||
if (codec->control_data)
|
||||
return -ENODEV;
|
||||
|
||||
/* Note: codec_dai->codec is NULL here */
|
||||
|
||||
codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);
|
||||
if (!codec->reg_cache) {
|
||||
printk(KERN_ERR "cs4270: could not allocate register cache\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Verify that we have a CS4270 */
|
||||
|
||||
ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to read I2C\n");
|
||||
goto error;
|
||||
}
|
||||
/* The top four bits of the chip ID should be 1100. */
|
||||
if ((ret & 0xF0) != 0xC0) {
|
||||
/* The device at this address is not a CS4270 codec */
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "cs4270: found device at I2C address %X\n",
|
||||
i2c_client->addr);
|
||||
printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF);
|
||||
|
||||
codec->control_data = i2c_client;
|
||||
codec->read = cs4270_read_reg_cache;
|
||||
codec->write = cs4270_i2c_write;
|
||||
codec->reg_cache_size = CS4270_NUMREGS;
|
||||
|
||||
/* The I2C interface is set up, so pre-fill our register cache */
|
||||
|
||||
ret = cs4270_fill_cache(codec);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to fill register cache\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Add the non-DAPM controls */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs4270_snd_controls); i++) {
|
||||
struct snd_kcontrol *kctrl =
|
||||
snd_soc_cnew(&cs4270_snd_controls[i], codec, NULL);
|
||||
|
||||
ret = snd_ctl_add(codec->card, kctrl);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c_client, codec);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
codec->control_data = NULL;
|
||||
|
||||
kfree(codec->reg_cache);
|
||||
codec->reg_cache = NULL;
|
||||
codec->reg_cache_size = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cs4270_id[] = {
|
||||
{"cs4270", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs4270_id);
|
||||
|
||||
static struct i2c_driver cs4270_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs4270",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = cs4270_id,
|
||||
.probe = cs4270_i2c_probe,
|
||||
};
|
||||
|
||||
struct snd_soc_dai cs4270_dai = {
|
||||
.name = "CS4270",
|
||||
.name = "cs4270",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
|
@ -624,31 +522,60 @@ struct snd_soc_dai cs4270_dai = {
|
|||
.rates = 0,
|
||||
.formats = CS4270_FORMATS,
|
||||
},
|
||||
.ops = {
|
||||
.hw_params = cs4270_hw_params,
|
||||
.set_sysclk = cs4270_set_dai_sysclk,
|
||||
.set_fmt = cs4270_set_dai_fmt,
|
||||
.digital_mute = cs4270_mute,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cs4270_dai);
|
||||
|
||||
/*
|
||||
* ASoC probe function
|
||||
* Initialize the I2C interface of the CS4270
|
||||
*
|
||||
* This function is called when the machine driver calls
|
||||
* platform_device_add().
|
||||
* This function is called for whenever the I2C subsystem finds a device
|
||||
* at a particular address.
|
||||
*
|
||||
* Note: snd_soc_new_pcms() must be called before this function can be called,
|
||||
* because of snd_ctl_add().
|
||||
*/
|
||||
static int cs4270_probe(struct platform_device *pdev)
|
||||
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_device *socdev = cs4270_socdev;
|
||||
struct snd_soc_codec *codec;
|
||||
struct cs4270_private *cs4270;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "CS4270 ALSA SoC Codec\n");
|
||||
/* Verify that we have a CS4270 */
|
||||
|
||||
ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to read I2C\n");
|
||||
return ret;
|
||||
}
|
||||
/* The top four bits of the chip ID should be 1100. */
|
||||
if ((ret & 0xF0) != 0xC0) {
|
||||
printk(KERN_ERR "cs4270: device at addr %X is not a CS4270\n",
|
||||
i2c_client->addr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "cs4270: found device at I2C address %X\n",
|
||||
i2c_client->addr);
|
||||
printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF);
|
||||
|
||||
/* Allocate enough space for the snd_soc_codec structure
|
||||
and our private data together. */
|
||||
codec = kzalloc(ALIGN(sizeof(struct snd_soc_codec), 4) +
|
||||
sizeof(struct cs4270_private), GFP_KERNEL);
|
||||
if (!codec) {
|
||||
cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL);
|
||||
if (!cs4270) {
|
||||
printk(KERN_ERR "cs4270: Could not allocate codec structure\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
codec = &cs4270->codec;
|
||||
socdev->codec = codec;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
|
@ -658,10 +585,20 @@ static int cs4270_probe(struct platform_device *pdev)
|
|||
codec->owner = THIS_MODULE;
|
||||
codec->dai = &cs4270_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->private_data = (void *) codec +
|
||||
ALIGN(sizeof(struct snd_soc_codec), 4);
|
||||
codec->private_data = cs4270;
|
||||
codec->control_data = i2c_client;
|
||||
codec->read = cs4270_read_reg_cache;
|
||||
codec->write = cs4270_i2c_write;
|
||||
codec->reg_cache = cs4270->reg_cache;
|
||||
codec->reg_cache_size = CS4270_NUMREGS;
|
||||
|
||||
socdev->codec = codec;
|
||||
/* The I2C interface is set up, so pre-fill our register cache */
|
||||
|
||||
ret = cs4270_fill_cache(codec);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to fill register cache\n");
|
||||
goto error_free_codec;
|
||||
}
|
||||
|
||||
/* Register PCMs */
|
||||
|
||||
|
@ -671,58 +608,93 @@ static int cs4270_probe(struct platform_device *pdev)
|
|||
goto error_free_codec;
|
||||
}
|
||||
|
||||
cs4270_socdev = socdev;
|
||||
/* Add the non-DAPM controls */
|
||||
|
||||
ret = i2c_add_driver(&cs4270_i2c_driver);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "cs4270: failed to attach driver");
|
||||
goto error_free_pcms;
|
||||
for (i = 0; i < ARRAY_SIZE(cs4270_snd_controls); i++) {
|
||||
struct snd_kcontrol *kctrl;
|
||||
|
||||
kctrl = snd_soc_cnew(&cs4270_snd_controls[i], codec, NULL);
|
||||
if (!kctrl) {
|
||||
printk(KERN_ERR "cs4270: error creating control '%s'\n",
|
||||
cs4270_snd_controls[i].name);
|
||||
ret = -ENOMEM;
|
||||
goto error_free_pcms;
|
||||
}
|
||||
|
||||
ret = snd_ctl_add(codec->card, kctrl);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: error adding control '%s'\n",
|
||||
cs4270_snd_controls[i].name);
|
||||
goto error_free_pcms;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we find a CS4270 on the I2C bus? */
|
||||
if (!codec->control_data) {
|
||||
printk(KERN_ERR "cs4270: failed to attach driver");
|
||||
goto error_del_driver;
|
||||
}
|
||||
|
||||
/* Initialize codec ops */
|
||||
cs4270_dai.ops.hw_params = cs4270_hw_params;
|
||||
cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
|
||||
cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
|
||||
cs4270_dai.ops.digital_mute = cs4270_mute;
|
||||
/* Initialize the SOC device */
|
||||
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to register card\n");
|
||||
goto error_del_driver;
|
||||
goto error_free_pcms;;
|
||||
}
|
||||
|
||||
return 0;
|
||||
i2c_set_clientdata(i2c_client, socdev);
|
||||
|
||||
error_del_driver:
|
||||
i2c_del_driver(&cs4270_i2c_driver);
|
||||
return 0;
|
||||
|
||||
error_free_pcms:
|
||||
snd_soc_free_pcms(socdev);
|
||||
|
||||
error_free_codec:
|
||||
kfree(socdev->codec);
|
||||
socdev->codec = NULL;
|
||||
kfree(cs4270);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs4270_remove(struct platform_device *pdev)
|
||||
static int cs4270_i2c_remove(struct i2c_client *i2c_client)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_device *socdev = i2c_get_clientdata(i2c_client);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct cs4270_private *cs4270 = codec->private_data;
|
||||
|
||||
snd_soc_free_pcms(socdev);
|
||||
kfree(cs4270);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id cs4270_id[] = {
|
||||
{"cs4270", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs4270_id);
|
||||
|
||||
static struct i2c_driver cs4270_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs4270",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = cs4270_id,
|
||||
.probe = cs4270_i2c_probe,
|
||||
.remove = cs4270_i2c_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
* ASoC probe function
|
||||
*
|
||||
* This function is called when the machine driver calls
|
||||
* platform_device_add().
|
||||
*/
|
||||
static int cs4270_probe(struct platform_device *pdev)
|
||||
{
|
||||
cs4270_socdev = platform_get_drvdata(pdev);;
|
||||
|
||||
return i2c_add_driver(&cs4270_i2c_driver);
|
||||
}
|
||||
|
||||
static int cs4270_remove(struct platform_device *pdev)
|
||||
{
|
||||
i2c_del_driver(&cs4270_i2c_driver);
|
||||
|
||||
kfree(socdev->codec);
|
||||
socdev->codec = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -740,6 +712,8 @@ EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
|
|||
|
||||
static int __init cs4270_init(void)
|
||||
{
|
||||
printk(KERN_INFO "Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
|
||||
|
||||
return snd_soc_register_dai(&cs4270_dai);
|
||||
}
|
||||
module_init(cs4270_init);
|
||||
|
|
Loading…
Reference in New Issue