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: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Mark Brown 2009-03-11 18:31:08 +00:00
parent 2a9f0ba7a9
commit 6f7cb44ba1
2 changed files with 166 additions and 181 deletions

View File

@ -1,7 +1,7 @@
/* /*
* wm8580.c -- WM8580 ALSA Soc Audio driver * 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 * 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 * under the terms of the GNU General Public License as published by the
@ -35,17 +35,6 @@
#include "wm8580.h" #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 */ /* WM8580 register space */
#define WM8580_PLLA1 0x00 #define WM8580_PLLA1 0x00
#define WM8580_PLLA2 0x01 #define WM8580_PLLA2 0x01
@ -100,6 +89,8 @@ struct wm8580_priv {
#define WM8580_READBACK 0x34 #define WM8580_READBACK 0x34
#define WM8580_RESET 0x35 #define WM8580_RESET 0x35
#define WM8580_MAX_REGISTER 0x35
/* PLLB4 (register 7h) */ /* PLLB4 (register 7h) */
#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 #define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60
#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 #define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20
@ -191,6 +182,20 @@ static const u16 wm8580_reg[] = {
0x0000, 0x0000 /*R53*/ 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 * read wm8580 register cache
*/ */
@ -755,8 +760,22 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
break; 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: case SND_SOC_BIAS_OFF:
reg = wm8580_read(codec, WM8580_PWRDN1); reg = wm8580_read(codec, WM8580_PWRDN1);
wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
@ -812,100 +831,163 @@ struct snd_soc_dai wm8580_dai[] = {
}; };
EXPORT_SYMBOL_GPL(wm8580_dai); EXPORT_SYMBOL_GPL(wm8580_dai);
/* static struct snd_soc_codec *wm8580_codec;
* initialise the WM8580 driver
* register the mixer and dsp interfaces with the kernel static int wm8580_probe(struct platform_device *pdev)
*/
static int wm8580_init(struct snd_soc_device *socdev)
{ {
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; int ret = 0;
codec->name = "WM8580"; if (wm8580_codec == NULL) {
codec->owner = THIS_MODULE; dev_err(&pdev->dev, "Codec device not registered\n");
codec->read = wm8580_read_reg_cache; return -ENODEV;
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) socdev->card->codec = wm8580_codec;
return -ENOMEM; codec = wm8580_codec;
/* 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));
/* Make VMID high impedence */
wm8580_write(codec, WM8580_ADC_CONTROL1,
wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100);
/* register pcms */ /* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
SNDRV_DEFAULT_STR1);
if (ret < 0) { 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; goto pcm_err;
} }
snd_soc_add_controls(codec, wm8580_snd_controls, snd_soc_add_controls(codec, wm8580_snd_controls,
ARRAY_SIZE(wm8580_snd_controls)); ARRAY_SIZE(wm8580_snd_controls));
wm8580_add_widgets(codec); wm8580_add_widgets(codec);
ret = snd_soc_init_card(socdev); ret = snd_soc_init_card(socdev);
if (ret < 0) { 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; goto card_err;
} }
return ret; return ret;
card_err: card_err:
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev); snd_soc_dapm_free(socdev);
pcm_err: pcm_err:
kfree(codec->reg_cache);
return ret; return ret;
} }
/* If the i2c layer weren't so broken, we could pass this kind of data /* power down chip */
around */ static int wm8580_remove(struct platform_device *pdev)
static struct snd_soc_device *wm8580_socdev; {
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
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)
{
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;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
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;
memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
/* Get the codec into a known state */
ret = wm8580_write(codec, WM8580_RESET, 0);
if (ret != 0) {
dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
goto err;
}
for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
wm8580_dai[i].dev = codec->dev;
wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
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;
}
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_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8580);
return ret;
}
static void wm8580_unregister(struct wm8580_priv *wm8580)
{
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;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #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, static int wm8580_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct snd_soc_device *socdev = wm8580_socdev; struct wm8580_priv *wm8580;
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec;
int ret;
i2c_set_clientdata(i2c, codec); wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
if (wm8580 == NULL)
return -ENOMEM;
codec = &wm8580->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8580);
codec->control_data = i2c; codec->control_data = i2c;
ret = wm8580_init(socdev); codec->dev = &i2c->dev;
if (ret < 0)
dev_err(&i2c->dev, "failed to initialise WM8580\n"); return wm8580_register(wm8580);
return ret;
} }
static int wm8580_i2c_remove(struct i2c_client *client) static int wm8580_i2c_remove(struct i2c_client *client)
{ {
struct snd_soc_codec *codec = i2c_get_clientdata(client); struct wm8580_priv *wm8580 = i2c_get_clientdata(client);
kfree(codec->reg_cache); wm8580_unregister(wm8580);
return 0; return 0;
} }
@ -917,127 +999,35 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
static struct i2c_driver wm8580_i2c_driver = { static struct i2c_driver wm8580_i2c_driver = {
.driver = { .driver = {
.name = "WM8580 I2C Codec", .name = "wm8580",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = wm8580_i2c_probe, .probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove, .remove = wm8580_i2c_remove,
.id_table = wm8580_i2c_id, .id_table = wm8580_i2c_id,
}; };
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;
ret = i2c_add_driver(&wm8580_i2c_driver);
if (ret != 0) {
dev_err(&pdev->dev, "can't add i2c driver\n");
return ret;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = setup->i2c_address;
strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
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;
}
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;
}
return 0;
err_driver:
i2c_del_driver(&wm8580_i2c_driver);
return -ENODEV;
}
#endif #endif
static int wm8580_probe(struct platform_device *pdev)
{
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;
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
if (wm8580 == NULL) {
kfree(codec);
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->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;
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);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_wm8580 = {
.probe = wm8580_probe,
.remove = wm8580_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
static int __init wm8580_modinit(void) 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); module_init(wm8580_modinit);
static void __exit wm8580_exit(void) 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); module_exit(wm8580_exit);

View File

@ -28,11 +28,6 @@
#define WM8580_CLKSRC_OSC 4 #define WM8580_CLKSRC_OSC 4
#define WM8580_CLKSRC_NONE 5 #define WM8580_CLKSRC_NONE 5
struct wm8580_setup_data {
int i2c_bus;
unsigned short i2c_address;
};
#define WM8580_DAI_PAIFRX 0 #define WM8580_DAI_PAIFRX 0
#define WM8580_DAI_PAIFTX 1 #define WM8580_DAI_PAIFTX 1