|
@@ -31,8 +31,6 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
|
|
-#include "cs4270.h"
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* The codec isn't really big-endian or little-endian, since the I2S
|
|
* The codec isn't really big-endian or little-endian, since the I2S
|
|
* interface requires data to be sent serially with the MSbit first.
|
|
* 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 */
|
|
/* Private data for the CS4270 */
|
|
struct cs4270_private {
|
|
struct cs4270_private {
|
|
- struct snd_soc_codec codec;
|
|
|
|
|
|
+ enum snd_soc_control_type control_type;
|
|
|
|
+ void *control_data;
|
|
u8 reg_cache[CS4270_NUMREGS];
|
|
u8 reg_cache[CS4270_NUMREGS];
|
|
unsigned int mclk; /* Input frequency of the MCLK pin */
|
|
unsigned int mclk; /* Input frequency of the MCLK pin */
|
|
unsigned int mode; /* The mode (I2S or left-justified) */
|
|
unsigned int mode; /* The mode (I2S or left-justified) */
|
|
@@ -212,44 +211,8 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
{
|
|
{
|
|
struct snd_soc_codec *codec = codec_dai->codec;
|
|
struct snd_soc_codec *codec = codec_dai->codec;
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
- unsigned int rates = 0;
|
|
|
|
- unsigned int rate_min = -1;
|
|
|
|
- unsigned int rate_max = 0;
|
|
|
|
- unsigned int i;
|
|
|
|
|
|
|
|
cs4270->mclk = freq;
|
|
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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -410,8 +373,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
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 snd_soc_codec *codec = rtd->codec;
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
int ret;
|
|
int ret;
|
|
unsigned int i;
|
|
unsigned int i;
|
|
@@ -549,19 +511,6 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
|
|
snd_soc_get_volsw, cs4270_soc_put_mute),
|
|
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 = {
|
|
static struct snd_soc_dai_ops cs4270_dai_ops = {
|
|
.hw_params = cs4270_hw_params,
|
|
.hw_params = cs4270_hw_params,
|
|
.set_sysclk = cs4270_set_dai_sysclk,
|
|
.set_sysclk = cs4270_set_dai_sysclk,
|
|
@@ -569,20 +518,24 @@ static struct snd_soc_dai_ops cs4270_dai_ops = {
|
|
.digital_mute = cs4270_dai_mute,
|
|
.digital_mute = cs4270_dai_mute,
|
|
};
|
|
};
|
|
|
|
|
|
-struct snd_soc_dai cs4270_dai = {
|
|
|
|
- .name = "cs4270",
|
|
|
|
|
|
+struct snd_soc_dai_driver cs4270_dai = {
|
|
|
|
+ .name = "cs4270-hifi",
|
|
.playback = {
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.stream_name = "Playback",
|
|
.channels_min = 1,
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.channels_max = 2,
|
|
- .rates = 0,
|
|
|
|
|
|
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
|
|
|
|
+ .rate_min = 4000,
|
|
|
|
+ .rate_max = 216000,
|
|
.formats = CS4270_FORMATS,
|
|
.formats = CS4270_FORMATS,
|
|
},
|
|
},
|
|
.capture = {
|
|
.capture = {
|
|
.stream_name = "Capture",
|
|
.stream_name = "Capture",
|
|
.channels_min = 1,
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.channels_max = 2,
|
|
- .rates = 0,
|
|
|
|
|
|
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
|
|
|
|
+ .rate_min = 4000,
|
|
|
|
+ .rate_max = 216000,
|
|
.formats = CS4270_FORMATS,
|
|
.formats = CS4270_FORMATS,
|
|
},
|
|
},
|
|
.ops = &cs4270_dai_ops,
|
|
.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
|
|
* This function is called when ASoC has all the pieces it needs to
|
|
* instantiate a sound driver.
|
|
* 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 = snd_soc_codec_get_drvdata(codec);
|
|
|
|
- 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)
|
|
|
|
|
|
+static int cs4270_probe(struct snd_soc_codec *codec)
|
|
{
|
|
{
|
|
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
|
|
- struct snd_soc_codec *codec = cs4270_codec;
|
|
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
|
|
+ int i, ret, reg;
|
|
|
|
|
|
- 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)
|
|
|
|
-{
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* 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;
|
|
|
|
- snd_soc_codec_set_drvdata(codec, 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 */
|
|
/* The I2C interface is set up, so pre-fill our register cache */
|
|
|
|
|
|
ret = cs4270_fill_cache(codec);
|
|
ret = cs4270_fill_cache(codec);
|
|
if (ret < 0) {
|
|
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
|
|
/* 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;
|
|
reg &= ~CS4270_MUTE_AUTO;
|
|
ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
|
|
ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
- dev_err(&i2c_client->dev, "i2c write failed\n");
|
|
|
|
|
|
+ dev_err(codec->dev, "i2c write failed\n");
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -769,65 +588,56 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
|
|
reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
|
|
reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
|
|
ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
|
|
ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
- dev_err(&i2c_client->dev, "i2c write failed\n");
|
|
|
|
|
|
+ dev_err(codec->dev, "i2c write failed\n");
|
|
return ret;
|
|
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) {
|
|
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;
|
|
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;
|
|
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;
|
|
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
|
|
#ifdef CONFIG_PM
|
|
|
|
|
|
@@ -840,9 +650,8 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
|
|
* and all registers are written back to the hardware when resuming.
|
|
* 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 = snd_soc_codec_get_drvdata(codec);
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
int reg, ret;
|
|
int reg, ret;
|
|
|
|
|
|
@@ -860,9 +669,8 @@ static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
return 0;
|
|
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 = snd_soc_codec_get_drvdata(codec);
|
|
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
|
|
struct i2c_client *i2c_client = codec->control_data;
|
|
struct i2c_client *i2c_client = codec->control_data;
|
|
int reg;
|
|
int reg;
|
|
@@ -895,6 +703,95 @@ static int cs4270_soc_resume(struct platform_device *pdev)
|
|
#define cs4270_soc_resume NULL
|
|
#define cs4270_soc_resume NULL
|
|
#endif /* CONFIG_PM */
|
|
#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
|
|
* 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 = {
|
|
static struct i2c_driver cs4270_i2c_driver = {
|
|
.driver = {
|
|
.driver = {
|
|
- .name = "cs4270",
|
|
|
|
|
|
+ .name = "cs4270-codec",
|
|
.owner = THIS_MODULE,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
},
|
|
.id_table = cs4270_id,
|
|
.id_table = cs4270_id,
|
|
@@ -911,20 +808,6 @@ static struct i2c_driver cs4270_i2c_driver = {
|
|
.remove = cs4270_i2c_remove,
|
|
.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)
|
|
static int __init cs4270_init(void)
|
|
{
|
|
{
|
|
pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
|
|
pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
|