]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - sound/soc/codecs/cs4270.c
ASoC: multi-component - ASoC Multi-Component Support
[mv-sheeva.git] / sound / soc / codecs / cs4270.c
index 81a62d198b7002b92f9df5d82c7cdd30b625b7c0..6542dc03895110bc3cd1010b0236286eccbc0f15 100644 (file)
@@ -31,8 +31,6 @@
 #include <linux/delay.h>
 #include <linux/regulator/consumer.h>
 
-#include "cs4270.h"
-
 /*
  * The codec isn't really big-endian or little-endian, since the I2S
  * interface requires data to be sent serially with the MSbit first.
@@ -114,7 +112,8 @@ static const char *supply_names[] = {
 
 /* Private data for the CS4270 */
 struct cs4270_private {
-       struct snd_soc_codec codec;
+       enum snd_soc_control_type control_type;
+       void *control_data;
        u8 reg_cache[CS4270_NUMREGS];
        unsigned int mclk; /* Input frequency of the MCLK pin */
        unsigned int mode; /* The mode (I2S or left-justified) */
@@ -211,45 +210,9 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                 int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct cs4270_private *cs4270 = codec->private_data;
-       unsigned int rates = 0;
-       unsigned int rate_min = -1;
-       unsigned int rate_max = 0;
-       unsigned int i;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
 
        cs4270->mclk = freq;
-
-       if (cs4270->mclk) {
-               for (i = 0; i < NUM_MCLK_RATIOS; i++) {
-                       unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
-                       rates |= snd_pcm_rate_to_rate_bit(rate);
-                       if (rate < rate_min)
-                               rate_min = rate;
-                       if (rate > rate_max)
-                               rate_max = rate;
-               }
-               /* FIXME: soc should support a rate list */
-               rates &= ~SNDRV_PCM_RATE_KNOT;
-
-               if (!rates) {
-                       dev_err(codec->dev, "could not find a valid sample rate\n");
-                       return -EINVAL;
-               }
-       } else {
-               /* enable all possible rates */
-               rates = SNDRV_PCM_RATE_8000_192000;
-               rate_min = 8000;
-               rate_max = 192000;
-       }
-
-       codec_dai->playback.rates = rates;
-       codec_dai->playback.rate_min = rate_min;
-       codec_dai->playback.rate_max = rate_max;
-
-       codec_dai->capture.rates = rates;
-       codec_dai->capture.rate_min = rate_min;
-       codec_dai->capture.rate_max = rate_max;
-
        return 0;
 }
 
@@ -270,7 +233,7 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
                              unsigned int format)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct cs4270_private *cs4270 = codec->private_data;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
 
        /* set DAI format */
@@ -410,9 +373,8 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
                            struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->card->codec;
-       struct cs4270_private *cs4270 = codec->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int ret;
        unsigned int i;
        unsigned int rate;
@@ -491,7 +453,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
 static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
-       struct cs4270_private *cs4270 = codec->private_data;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int reg6;
 
        reg6 = snd_soc_read(codec, CS4270_MUTE);
@@ -524,7 +486,7 @@ 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 cs4270_private *cs4270 = codec->private_data;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int left = !ucontrol->value.integer.value[0];
        int right = !ucontrol->value.integer.value[1];
 
@@ -549,19 +511,6 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
                snd_soc_get_volsw, cs4270_soc_put_mute),
 };
 
-/*
- * cs4270_codec - 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_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_codec *cs4270_codec;
-
 static struct snd_soc_dai_ops cs4270_dai_ops = {
        .hw_params      = cs4270_hw_params,
        .set_sysclk     = cs4270_set_dai_sysclk,
@@ -569,20 +518,24 @@ static struct snd_soc_dai_ops cs4270_dai_ops = {
        .digital_mute   = cs4270_dai_mute,
 };
 
-struct snd_soc_dai cs4270_dai = {
-       .name = "cs4270",
+struct snd_soc_dai_driver cs4270_dai = {
+       .name = "cs4270-hifi",
        .playback = {
                .stream_name = "Playback",
                .channels_min = 1,
                .channels_max = 2,
-               .rates = 0,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min = 4000,
+               .rate_max = 216000,
                .formats = CS4270_FORMATS,
        },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
-               .rates = 0,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min = 4000,
+               .rate_max = 216000,
                .formats = CS4270_FORMATS,
        },
        .ops = &cs4270_dai_ops,
@@ -596,153 +549,19 @@ EXPORT_SYMBOL_GPL(cs4270_dai);
  * This function is called when ASoC has all the pieces it needs to
  * instantiate a sound driver.
  */
-static int cs4270_probe(struct platform_device *pdev)
-{
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = cs4270_codec;
-       struct cs4270_private *cs4270 = codec->private_data;
-       int i, 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) {
-               dev_err(codec->dev, "failed to create pcms\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");
-               goto error_free_pcms;
-       }
-
-       /* 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)
-               goto error_free_pcms;
-
-       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);
-
-error_free_pcms:
-       snd_soc_free_pcms(socdev);
-
-       return ret;
-}
-
-/**
- * cs4270_remove - ASoC remove function
- * @pdev: platform device
- *
- * This function is the counterpart to cs4270_probe().
- */
-static int cs4270_remove(struct platform_device *pdev)
-{
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = cs4270_codec;
-       struct cs4270_private *cs4270 = codec->private_data;
-
-       snd_soc_free_pcms(socdev);
-       regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
-       regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
-
-       return 0;
-};
-
-/**
- * cs4270_i2c_probe - initialize the I2C interface of the CS4270
- * @i2c_client: the I2C client object
- * @id: the I2C device ID (ignored)
- *
- * This function is called whenever the I2C subsystem finds a device that
- * matches the device ID given via a prior call to i2c_add_driver().
- */
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
-       const struct i2c_device_id *id)
+static int cs4270_probe(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec;
-       struct cs4270_private *cs4270;
-       unsigned int reg;
-       int ret;
-
-       /* For now, we only support one cs4270 device in the system.  See the
-        * comment for cs4270_codec.
-        */
-       if (cs4270_codec) {
-               dev_err(&i2c_client->dev, "ignoring CS4270 at addr %X\n",
-                      i2c_client->addr);
-               dev_err(&i2c_client->dev, "only one per board allowed\n");
-               /* Should we return something other than ENODEV here? */
-               return -ENODEV;
-       }
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+       int i, ret, reg;
 
-       /* Verify that we have a CS4270 */
-
-       ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
-       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) {
-               dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
-                      i2c_client->addr);
-               return -ENODEV;
-       }
-
-       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);
-
-       /* Allocate enough space for the snd_soc_codec structure
-          and our private data together. */
-       cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL);
-       if (!cs4270) {
-               dev_err(&i2c_client->dev, "could not allocate codec\n");
-               return -ENOMEM;
-       }
-       codec = &cs4270->codec;
-
-       mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
-
-       codec->dev = &i2c_client->dev;
-       codec->name = "CS4270";
-       codec->owner = THIS_MODULE;
-       codec->dai = &cs4270_dai;
-       codec->num_dai = 1;
-       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;
+       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(&i2c_client->dev, "failed to fill register cache\n");
-               goto error_free_codec;
+               dev_err(codec->dev, "failed to fill register cache\n");
+               return ret;
        }
 
        /* Disable auto-mute.  This feature appears to be buggy.  In some
@@ -755,7 +574,7 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
        reg &= ~CS4270_MUTE_AUTO;
        ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
        if (ret < 0) {
-               dev_err(&i2c_client->dev, "i2c write failed\n");
+               dev_err(codec->dev, "i2c write failed\n");
                return ret;
        }
 
@@ -769,65 +588,56 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
        reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
        ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
        if (ret < 0) {
-               dev_err(&i2c_client->dev, "i2c write failed\n");
+               dev_err(codec->dev, "i2c write failed\n");
                return ret;
        }
 
-       /* Initialize the DAI. Normally, we'd prefer to have a kmalloc'd DAI
-        * structure for each CS4270 device, but the machine driver needs to
-        * have a pointer to the DAI structure, so for now it must be a global
-        * variable.
-        */
-       cs4270_dai.dev = &i2c_client->dev;
-
-       /* 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.
-        */
-       cs4270_codec = codec;
-       ret = snd_soc_register_dai(&cs4270_dai);
+       /* Add the non-DAPM controls */
+       ret = snd_soc_add_controls(codec, cs4270_snd_controls,
+                               ARRAY_SIZE(cs4270_snd_controls));
        if (ret < 0) {
-               dev_err(&i2c_client->dev, "failed to register DAIe\n");
-               goto error_free_codec;
+               dev_err(codec->dev, "failed to add controls\n");
+               return ret;
        }
 
-       i2c_set_clientdata(i2c_client, cs4270);
+       /* 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_codec:
-       kfree(cs4270);
-       cs4270_codec = NULL;
-       cs4270_dai.dev = NULL;
+error_free_regulators:
+       regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
+                           cs4270->supplies);
 
        return ret;
 }
 
 /**
- * cs4270_i2c_remove - remove an I2C device
- * @i2c_client: the I2C client object
+ * cs4270_remove - ASoC remove function
+ * @pdev: platform device
  *
- * This function is the counterpart to cs4270_i2c_probe().
+ * This function is the counterpart to cs4270_probe().
  */
-static int cs4270_i2c_remove(struct i2c_client *i2c_client)
+static int cs4270_remove(struct snd_soc_codec *codec)
 {
-       struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
 
-       kfree(cs4270);
-       cs4270_codec = NULL;
-       cs4270_dai.dev = NULL;
+       regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
+       regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
 
        return 0;
-}
-
-/*
- * cs4270_id - I2C device IDs supported by this driver
- */
-static struct i2c_device_id cs4270_id[] = {
-       {"cs4270", 0},
-       {}
 };
-MODULE_DEVICE_TABLE(i2c, cs4270_id);
 
 #ifdef CONFIG_PM
 
@@ -840,10 +650,9 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
  * and all registers are written back to the hardware when resuming.
  */
 
-static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
 {
-       struct snd_soc_codec *codec = cs4270_codec;
-       struct cs4270_private *cs4270 = codec->private_data;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int reg, ret;
 
        reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
@@ -860,10 +669,9 @@ static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
        return 0;
 }
 
-static int cs4270_soc_resume(struct platform_device *pdev)
+static int cs4270_soc_resume(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec = cs4270_codec;
-       struct cs4270_private *cs4270 = codec->private_data;
+       struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        struct i2c_client *i2c_client = codec->control_data;
        int reg;
 
@@ -895,6 +703,95 @@ static int cs4270_soc_resume(struct platform_device *pdev)
 #define cs4270_soc_resume      NULL
 #endif /* CONFIG_PM */
 
+/*
+ * ASoC codec device structure
+ *
+ * Assign this variable to the codec_dev field of the machine driver's
+ * snd_soc_device 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),
+};
+
+/**
+ * cs4270_i2c_probe - initialize the I2C interface of the CS4270
+ * @i2c_client: the I2C client object
+ * @id: the I2C device ID (ignored)
+ *
+ * This function is called whenever the I2C subsystem finds a device that
+ * matches the device ID given via a prior call to i2c_add_driver().
+ */
+static int cs4270_i2c_probe(struct i2c_client *i2c_client,
+       const struct i2c_device_id *id)
+{
+       struct cs4270_private *cs4270;
+       int ret;
+
+       /* Verify that we have a CS4270 */
+
+       ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
+       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) {
+               dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
+                      i2c_client->addr);
+               return -ENODEV;
+       }
+
+       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;
+       }
+
+       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;
+}
+
+/**
+ * cs4270_i2c_remove - remove an I2C device
+ * @i2c_client: the I2C client object
+ *
+ * This function is the counterpart to cs4270_i2c_probe().
+ */
+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[] = {
+       {"cs4270", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4270_id);
+
 /*
  * cs4270_i2c_driver - I2C device identification
  *
@@ -903,7 +800,7 @@ static int cs4270_soc_resume(struct platform_device *pdev)
  */
 static struct i2c_driver cs4270_i2c_driver = {
        .driver = {
-               .name = "cs4270",
+               .name = "cs4270-codec",
                .owner = THIS_MODULE,
        },
        .id_table = cs4270_id,
@@ -911,20 +808,6 @@ static struct i2c_driver cs4270_i2c_driver = {
        .remove = cs4270_i2c_remove,
 };
 
-/*
- * ASoC codec device structure
- *
- * Assign this variable to the codec_dev field of the machine driver's
- * snd_soc_device structure.
- */
-struct snd_soc_codec_device soc_codec_device_cs4270 = {
-       .probe =        cs4270_probe,
-       .remove =       cs4270_remove,
-       .suspend =      cs4270_soc_suspend,
-       .resume =       cs4270_soc_resume,
-};
-EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
-
 static int __init cs4270_init(void)
 {
        pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");