Commit 0db4d070 authored by Timur Tabi's avatar Timur Tabi Committed by Mark Brown
Browse files

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: default avatarTimur Tabi <timur@freescale.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 070504ad
Loading
Loading
Loading
Loading
+113 −139
Original line number Diff line number Diff line
@@ -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,6 +506,31 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
 */
static struct snd_soc_device *cs4270_socdev;

struct snd_soc_dai cs4270_dai = {
	.name = "cs4270",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = 0,
		.formats = CS4270_FORMATS,
	},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.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);

/*
 * Initialize the I2C interface of the CS4270
 *
@@ -517,47 +544,52 @@ 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;
	struct snd_soc_codec *codec;
	struct cs4270_private *cs4270;
	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;
		return ret;
	}
	/* 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_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. */
	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);
	INIT_LIST_HEAD(&codec->dapm_paths);

	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;

	/* The I2C interface is set up, so pre-fill our register cache */
@@ -565,35 +597,72 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
	ret = cs4270_fill_cache(codec);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: failed to fill register cache\n");
		goto error;
		goto error_free_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");
		goto error_free_codec;
	}

	/* 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);
		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)
			goto error;
		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, codec);
	i2c_set_clientdata(i2c_client, socdev);

	return 0;

error:
	codec->control_data = NULL;
error_free_pcms:
	snd_soc_free_pcms(socdev);

	kfree(codec->reg_cache);
	codec->reg_cache = NULL;
	codec->reg_cache_size = 0;
error_free_codec:
	kfree(cs4270);

	return ret;
}

static const struct i2c_device_id cs4270_id[] = {
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->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},
	{}
};
@@ -606,27 +675,9 @@ static struct i2c_driver cs4270_i2c_driver = {
	},
	.id_table = cs4270_id,
	.probe = cs4270_i2c_probe,
	.remove = cs4270_i2c_remove,
};

struct snd_soc_dai cs4270_dai = {
	.name = "CS4270",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = 0,
		.formats = CS4270_FORMATS,
	},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = 0,
		.formats = CS4270_FORMATS,
	},
};
EXPORT_SYMBOL_GPL(cs4270_dai);

/*
 * ASoC probe function
 *
@@ -635,94 +686,15 @@ EXPORT_SYMBOL_GPL(cs4270_dai);
 */
static int cs4270_probe(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec;
	int ret = 0;

	printk(KERN_INFO "CS4270 ALSA SoC Codec\n");

	/* 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) {
		printk(KERN_ERR "cs4270: Could not allocate codec structure\n");
		return -ENOMEM;
	}

	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	codec->name = "CS4270";
	codec->owner = THIS_MODULE;
	codec->dai = &cs4270_dai;
	codec->num_dai = 1;
	codec->private_data = (void *) codec +
		ALIGN(sizeof(struct snd_soc_codec), 4);

	socdev->codec = codec;
	cs4270_socdev = platform_get_drvdata(pdev);;

	/* 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");
		goto error_free_codec;
	}

	cs4270_socdev = socdev;

	ret = i2c_add_driver(&cs4270_i2c_driver);
	if (ret) {
		printk(KERN_ERR "cs4270: failed to attach driver");
		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;

	ret = snd_soc_init_card(socdev);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: failed to register card\n");
		goto error_del_driver;
	}

	return 0;

error_del_driver:
	i2c_del_driver(&cs4270_i2c_driver);

error_free_pcms:
	snd_soc_free_pcms(socdev);

error_free_codec:
	kfree(socdev->codec);
	socdev->codec = NULL;

	return ret;
	return i2c_add_driver(&cs4270_i2c_driver);
}

static int cs4270_remove(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);

	snd_soc_free_pcms(socdev);

	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);