Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f7cb44b authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: Move WM8580 to normal I2C device probe



Refactor the WM8580 device registration to probe via standard I2C device
registration, registering the DAIs once the device has probed via I2C.

Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 2a9f0ba7
Loading
Loading
Loading
Loading
+158 −168
Original line number Diff line number Diff line
/*
 * wm8580.c  --  WM8580 ALSA Soc Audio driver
 *
 * Copyright 2008 Wolfson Microelectronics PLC.
 * Copyright 2008, 2009 Wolfson Microelectronics PLC.
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
@@ -35,17 +35,6 @@

#include "wm8580.h"

struct pll_state {
	unsigned int in;
	unsigned int out;
};

/* codec private data */
struct wm8580_priv {
	struct pll_state a;
	struct pll_state b;
};

/* WM8580 register space */
#define WM8580_PLLA1                         0x00
#define WM8580_PLLA2                         0x01
@@ -100,6 +89,8 @@ struct wm8580_priv {
#define WM8580_READBACK                      0x34
#define WM8580_RESET                         0x35

#define WM8580_MAX_REGISTER                  0x35

/* PLLB4 (register 7h) */
#define WM8580_PLLB4_MCLKOUTSRC_MASK   0x60
#define WM8580_PLLB4_MCLKOUTSRC_PLLA   0x20
@@ -191,6 +182,20 @@ static const u16 wm8580_reg[] = {
	0x0000, 0x0000 /*R53*/
};

struct pll_state {
	unsigned int in;
	unsigned int out;
};

/* codec private data */
struct wm8580_priv {
	struct snd_soc_codec codec;
	u16 reg_cache[WM8580_MAX_REGISTER + 1];
	struct pll_state a;
	struct pll_state b;
};


/*
 * read wm8580 register cache
 */
@@ -755,8 +760,22 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
	switch (level) {
	case SND_SOC_BIAS_ON:
	case SND_SOC_BIAS_PREPARE:
		break;

	case SND_SOC_BIAS_STANDBY:
		if (codec->bias_level == SND_SOC_BIAS_OFF) {
			/* Power up and get individual control of the DACs */
			reg = wm8580_read(codec, WM8580_PWRDN1);
			reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
			wm8580_write(codec, WM8580_PWRDN1, reg);

			/* Make VMID high impedence */
			reg = wm8580_read(codec,  WM8580_ADC_CONTROL1);
			reg &= ~0x100;
			wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
		}
		break;

	case SND_SOC_BIAS_OFF:
		reg = wm8580_read(codec, WM8580_PWRDN1);
		wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
@@ -812,232 +831,203 @@ struct snd_soc_dai wm8580_dai[] = {
};
EXPORT_SYMBOL_GPL(wm8580_dai);

/*
 * initialise the WM8580 driver
 * register the mixer and dsp interfaces with the kernel
 */
static int wm8580_init(struct snd_soc_device *socdev)
static struct snd_soc_codec *wm8580_codec;

static int wm8580_probe(struct platform_device *pdev)
{
	struct snd_soc_codec *codec = socdev->card->codec;
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec;
	int ret = 0;

	codec->name = "WM8580";
	codec->owner = THIS_MODULE;
	codec->read = wm8580_read_reg_cache;
	codec->write = wm8580_write;
	codec->set_bias_level = wm8580_set_bias_level;
	codec->dai = wm8580_dai;
	codec->num_dai = ARRAY_SIZE(wm8580_dai);
	codec->reg_cache_size = ARRAY_SIZE(wm8580_reg);
	codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg),
				   GFP_KERNEL);

	if (codec->reg_cache == NULL)
		return -ENOMEM;

	/* Get the codec into a known state */
	wm8580_write(codec, WM8580_RESET, 0);

	/* Power up and get individual control of the DACs */
	wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) &
		     ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD));
	if (wm8580_codec == NULL) {
		dev_err(&pdev->dev, "Codec device not registered\n");
		return -ENODEV;
	}

	/* Make VMID high impedence */
	wm8580_write(codec, WM8580_ADC_CONTROL1,
		     wm8580_read(codec,  WM8580_ADC_CONTROL1) & ~0x100);
	socdev->card->codec = wm8580_codec;
	codec = wm8580_codec;

	/* register pcms */
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
			       SNDRV_DEFAULT_STR1);
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
	if (ret < 0) {
		printk(KERN_ERR "wm8580: failed to create pcms\n");
		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
		goto pcm_err;
	}

	snd_soc_add_controls(codec, wm8580_snd_controls,
			     ARRAY_SIZE(wm8580_snd_controls));
	wm8580_add_widgets(codec);

	ret = snd_soc_init_card(socdev);
	if (ret < 0) {
		printk(KERN_ERR "wm8580: failed to register card\n");
		dev_err(codec->dev, "failed to register card: %d\n", ret);
		goto card_err;
	}

	return ret;

card_err:
	snd_soc_free_pcms(socdev);
	snd_soc_dapm_free(socdev);
pcm_err:
	kfree(codec->reg_cache);
	return ret;
}

/* If the i2c layer weren't so broken, we could pass this kind of data
   around */
static struct snd_soc_device *wm8580_socdev;

#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

/*
 * WM8580 2 wire address is determined by GPIO5
 * state during powerup.
 *    low  = 0x1a
 *    high = 0x1b
 */

static int wm8580_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
/* power down chip */
static int wm8580_remove(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = wm8580_socdev;
	struct snd_soc_codec *codec = socdev->card->codec;
	int ret;
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);

	i2c_set_clientdata(i2c, codec);
	codec->control_data = i2c;
	snd_soc_free_pcms(socdev);
	snd_soc_dapm_free(socdev);

	ret = wm8580_init(socdev);
	if (ret < 0)
		dev_err(&i2c->dev, "failed to initialise WM8580\n");
	return ret;
	return 0;
}

static int wm8580_i2c_remove(struct i2c_client *client)
struct snd_soc_codec_device soc_codec_dev_wm8580 = {
	.probe = 	wm8580_probe,
	.remove = 	wm8580_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);

static int wm8580_register(struct wm8580_priv *wm8580)
{
	struct snd_soc_codec *codec = i2c_get_clientdata(client);
	kfree(codec->reg_cache);
	return 0;
	int ret, i;
	struct snd_soc_codec *codec = &wm8580->codec;

	if (wm8580_codec) {
		dev_err(codec->dev, "Another WM8580 is registered\n");
		ret = -EINVAL;
		goto err;
	}

static const struct i2c_device_id wm8580_i2c_id[] = {
	{ "wm8580", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

static struct i2c_driver wm8580_i2c_driver = {
	.driver = {
		.name = "WM8580 I2C Codec",
		.owner = THIS_MODULE,
	},
	.probe =    wm8580_i2c_probe,
	.remove =   wm8580_i2c_remove,
	.id_table = wm8580_i2c_id,
};
	codec->private_data = wm8580;
	codec->name = "WM8580";
	codec->owner = THIS_MODULE;
	codec->read = wm8580_read_reg_cache;
	codec->write = wm8580_write;
	codec->bias_level = SND_SOC_BIAS_OFF;
	codec->set_bias_level = wm8580_set_bias_level;
	codec->dai = wm8580_dai;
	codec->num_dai = ARRAY_SIZE(wm8580_dai);
	codec->reg_cache_size = ARRAY_SIZE(wm8580->reg_cache);
	codec->reg_cache = &wm8580->reg_cache;

static int wm8580_add_i2c_device(struct platform_device *pdev,
				 const struct wm8580_setup_data *setup)
{
	struct i2c_board_info info;
	struct i2c_adapter *adapter;
	struct i2c_client *client;
	int ret;
	memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));

	ret = i2c_add_driver(&wm8580_i2c_driver);
	/* Get the codec into a known state */
	ret = wm8580_write(codec, WM8580_RESET, 0);
	if (ret != 0) {
		dev_err(&pdev->dev, "can't add i2c driver\n");
		return ret;
		dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
		goto err;
	}

	memset(&info, 0, sizeof(struct i2c_board_info));
	info.addr = setup->i2c_address;
	strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
	for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
		wm8580_dai[i].dev = codec->dev;

	wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	adapter = i2c_get_adapter(setup->i2c_bus);
	if (!adapter) {
		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
			setup->i2c_bus);
		goto err_driver;
	wm8580_codec = codec;

	ret = snd_soc_register_codec(codec);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
		goto err;
	}

	client = i2c_new_device(adapter, &info);
	i2c_put_adapter(adapter);
	if (!client) {
		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
			(unsigned int)info.addr);
		goto err_driver;
	ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
		goto err_codec;
	}

	return 0;

err_driver:
	i2c_del_driver(&wm8580_i2c_driver);
	return -ENODEV;
err_codec:
	snd_soc_unregister_codec(codec);
err:
	kfree(wm8580);
	return ret;
}
#endif

static int wm8580_probe(struct platform_device *pdev)
static void wm8580_unregister(struct wm8580_priv *wm8580)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct wm8580_setup_data *setup;
	struct snd_soc_codec *codec;
	struct wm8580_priv *wm8580;
	int ret = 0;
	wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
	snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
	snd_soc_unregister_codec(&wm8580->codec);
	kfree(wm8580);
	wm8580_codec = NULL;
}

	setup = socdev->codec_data;
	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
	if (codec == NULL)
		return -ENOMEM;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8580_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct wm8580_priv *wm8580;
	struct snd_soc_codec *codec;

	wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
	if (wm8580 == NULL) {
		kfree(codec);
	if (wm8580 == NULL)
		return -ENOMEM;
	}

	codec->private_data = wm8580;
	socdev->card->codec = codec;
	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);
	wm8580_socdev = socdev;

#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
	if (setup->i2c_address) {
	codec = &wm8580->codec;
	codec->hw_write = (hw_write_t)i2c_master_send;
		ret = wm8580_add_i2c_device(pdev, setup);
	}
#else
		/* Add other interfaces here */
#endif
	return ret;
}

/* power down chip */
static int wm8580_remove(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec = socdev->card->codec;
	i2c_set_clientdata(i2c, wm8580);
	codec->control_data = i2c;

	if (codec->control_data)
		wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
	snd_soc_free_pcms(socdev);
	snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
	i2c_unregister_device(codec->control_data);
	i2c_del_driver(&wm8580_i2c_driver);
#endif
	kfree(codec->private_data);
	kfree(codec);
	codec->dev = &i2c->dev;

	return wm8580_register(wm8580);
}

static int wm8580_i2c_remove(struct i2c_client *client)
{
	struct wm8580_priv *wm8580 = i2c_get_clientdata(client);
	wm8580_unregister(wm8580);
	return 0;
}

struct snd_soc_codec_device soc_codec_dev_wm8580 = {
	.probe = 	wm8580_probe,
	.remove = 	wm8580_remove,
static const struct i2c_device_id wm8580_i2c_id[] = {
	{ "wm8580", 0 },
	{ }
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);

static struct i2c_driver wm8580_i2c_driver = {
	.driver = {
		.name = "wm8580",
		.owner = THIS_MODULE,
	},
	.probe =    wm8580_i2c_probe,
	.remove =   wm8580_i2c_remove,
	.id_table = wm8580_i2c_id,
};
#endif

static int __init wm8580_modinit(void)
{
	return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
	int ret;

#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
	ret = i2c_add_driver(&wm8580_i2c_driver);
	if (ret != 0) {
		pr_err("Failed to register WM8580 I2C driver: %d\n", ret);
	}
#endif

	return 0;
}
module_init(wm8580_modinit);

static void __exit wm8580_exit(void)
{
	snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
	i2c_del_driver(&wm8580_i2c_driver);
#endif
}
module_exit(wm8580_exit);

+0 −5
Original line number Diff line number Diff line
@@ -28,11 +28,6 @@
#define WM8580_CLKSRC_OSC  4
#define WM8580_CLKSRC_NONE 5

struct wm8580_setup_data {
	int i2c_bus;
	unsigned short i2c_address;
};

#define WM8580_DAI_PAIFRX 0
#define WM8580_DAI_PAIFTX 1