|
@@ -490,21 +490,17 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
- * Global variable to store socdev for i2c probe function.
|
|
|
+ * Global variable to store codec for the ASoC probe function.
|
|
|
*
|
|
|
* If struct i2c_driver had a private_data field, we wouldn't need to use
|
|
|
- * cs4270_socdec. This is the only way to pass the socdev structure to
|
|
|
- * cs4270_i2c_probe().
|
|
|
- *
|
|
|
- * The real solution to cs4270_socdev is to create a mechanism
|
|
|
- * that maps I2C addresses to snd_soc_device structures. Perhaps the
|
|
|
- * creation of the snd_soc_device object should be moved out of
|
|
|
- * cs4270_probe() and into cs4270_i2c_probe(), but that would make this
|
|
|
- * driver dependent on I2C. The CS4270 supports "stand-alone" mode, whereby
|
|
|
- * the chip is *not* connected to the I2C bus, but is instead configured via
|
|
|
- * input pins.
|
|
|
+ * cs4270_codec. This is the only way to pass the codec structure from
|
|
|
+ * cs4270_i2c_probe() to cs4270_probe(). Unfortunately, there is no good
|
|
|
+ * way to synchronize these two functions. cs4270_i2c_probe() can be called
|
|
|
+ * multiple times before cs4270_probe() is called even once. So for now, we
|
|
|
+ * also only allow cs4270_i2c_probe() to be run once. That means that we do
|
|
|
+ * not support more than one cs4270 device in the system, at least for now.
|
|
|
*/
|
|
|
-static struct snd_soc_device *cs4270_socdev;
|
|
|
+static struct snd_soc_codec *cs4270_codec;
|
|
|
|
|
|
struct snd_soc_dai cs4270_dai = {
|
|
|
.name = "cs4270",
|
|
@@ -531,6 +527,70 @@ struct snd_soc_dai cs4270_dai = {
|
|
|
};
|
|
|
EXPORT_SYMBOL_GPL(cs4270_dai);
|
|
|
|
|
|
+/*
|
|
|
+ * ASoC probe function
|
|
|
+ */
|
|
|
+static int cs4270_probe(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
|
+ struct snd_soc_codec *codec = cs4270_codec;
|
|
|
+ unsigned int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
|
|
|
+ socdev->card->codec = codec;
|
|
|
+
|
|
|
+ /* Register PCMs */
|
|
|
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
|
|
+ if (ret < 0) {
|
|
|
+ printk(KERN_ERR "cs4270: failed to create PCMs\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Add the non-DAPM controls */
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* And finally, register the socdev */
|
|
|
+ ret = snd_soc_init_card(socdev);
|
|
|
+ if (ret < 0) {
|
|
|
+ printk(KERN_ERR "cs4270: failed to register card\n");
|
|
|
+ goto error_free_pcms;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error_free_pcms:
|
|
|
+ snd_soc_free_pcms(socdev);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int cs4270_remove(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
|
+
|
|
|
+ snd_soc_free_pcms(socdev);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* Initialize the I2C interface of the CS4270
|
|
|
*
|
|
@@ -543,17 +603,27 @@ EXPORT_SYMBOL_GPL(cs4270_dai);
|
|
|
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;
|
|
|
struct cs4270_private *cs4270;
|
|
|
- int i;
|
|
|
- int ret = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* For now, we only support one cs4270 device in the system. See the
|
|
|
+ * comment for cs4270_codec.
|
|
|
+ */
|
|
|
+ if (cs4270_codec) {
|
|
|
+ printk(KERN_ERR "cs4270: ignoring CS4270 at addr %X\n",
|
|
|
+ i2c_client->addr);
|
|
|
+ printk(KERN_ERR "cs4270: only one CS4270 per board allowed\n");
|
|
|
+ /* Should we return something other than ENODEV here? */
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
/* 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");
|
|
|
+ printk(KERN_ERR "cs4270: failed to read I2C at addr %X\n",
|
|
|
+ i2c_client->addr);
|
|
|
return ret;
|
|
|
}
|
|
|
/* The top four bits of the chip ID should be 1100. */
|
|
@@ -575,7 +645,7 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
codec = &cs4270->codec;
|
|
|
- socdev->card->codec = codec;
|
|
|
+ cs4270_codec = codec;
|
|
|
|
|
|
mutex_init(&codec->mutex);
|
|
|
INIT_LIST_HEAD(&codec->dapm_widgets);
|
|
@@ -600,50 +670,20 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
|
|
|
goto error_free_codec;
|
|
|
}
|
|
|
|
|
|
- /* Register PCMs */
|
|
|
-
|
|
|
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
|
|
+ /* Register the DAI. If all the other ASoC driver have already
|
|
|
+ * registered, then this will call our probe function, so
|
|
|
+ * cs4270_codec needs to be ready.
|
|
|
+ */
|
|
|
+ ret = snd_soc_register_dai(&cs4270_dai);
|
|
|
if (ret < 0) {
|
|
|
- printk(KERN_ERR "cs4270: failed to create PCMs\n");
|
|
|
+ printk(KERN_ERR "cs4270: failed to register DAIe\n");
|
|
|
goto error_free_codec;
|
|
|
}
|
|
|
|
|
|
- /* Add the non-DAPM controls */
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Initialize the SOC device */
|
|
|
-
|
|
|
- ret = snd_soc_init_card(socdev);
|
|
|
- if (ret < 0) {
|
|
|
- printk(KERN_ERR "cs4270: failed to register card\n");
|
|
|
- goto error_free_pcms;;
|
|
|
- }
|
|
|
-
|
|
|
- i2c_set_clientdata(i2c_client, socdev);
|
|
|
+ i2c_set_clientdata(i2c_client, cs4270);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-error_free_pcms:
|
|
|
- snd_soc_free_pcms(socdev);
|
|
|
-
|
|
|
error_free_codec:
|
|
|
kfree(cs4270);
|
|
|
|
|
@@ -652,11 +692,8 @@ error_free_codec:
|
|
|
|
|
|
static int cs4270_i2c_remove(struct i2c_client *i2c_client)
|
|
|
{
|
|
|
- struct snd_soc_device *socdev = i2c_get_clientdata(i2c_client);
|
|
|
- struct snd_soc_codec *codec = socdev->card->codec;
|
|
|
- struct cs4270_private *cs4270 = codec->private_data;
|
|
|
+ struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
|
|
|
|
|
|
- snd_soc_free_pcms(socdev);
|
|
|
kfree(cs4270);
|
|
|
|
|
|
return 0;
|
|
@@ -678,26 +715,6 @@ static struct i2c_driver cs4270_i2c_driver = {
|
|
|
.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);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* ASoC codec device structure
|
|
|
*
|
|
@@ -714,13 +731,13 @@ static int __init cs4270_init(void)
|
|
|
{
|
|
|
printk(KERN_INFO "Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
|
|
|
|
|
|
- return snd_soc_register_dai(&cs4270_dai);
|
|
|
+ return i2c_add_driver(&cs4270_i2c_driver);
|
|
|
}
|
|
|
module_init(cs4270_init);
|
|
|
|
|
|
static void __exit cs4270_exit(void)
|
|
|
{
|
|
|
- snd_soc_unregister_dai(&cs4270_dai);
|
|
|
+ i2c_del_driver(&cs4270_i2c_driver);
|
|
|
}
|
|
|
module_exit(cs4270_exit);
|
|
|
|