ASoC: Convert WM8900 to do more work at I2C probe time

Redo the instantiation of the WM8900 to do most of the initialisation
work when the I2C driver probes rather than when the ASoC device is
instantiated, registering the codec with the ASoC core when done.

Also move all dynamic allocations into a single kmalloc() to simplify
error handling and rename the I2C driver to make output more sensible.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Mark Brown 2008-12-10 15:38:36 +00:00
parent 0d0cf00a7f
commit 78e19a39d3
2 changed files with 87 additions and 91 deletions

View File

@ -138,6 +138,10 @@
struct snd_soc_codec_device soc_codec_dev_wm8900; struct snd_soc_codec_device soc_codec_dev_wm8900;
struct wm8900_priv { struct wm8900_priv {
struct snd_soc_codec codec;
u16 reg_cache[WM8900_MAXREG];
u32 fll_in; /* FLL input frequency */ u32 fll_in; /* FLL input frequency */
u32 fll_out; /* FLL output frequency */ u32 fll_out; /* FLL output frequency */
}; };
@ -1282,16 +1286,28 @@ static int wm8900_resume(struct platform_device *pdev)
return 0; return 0;
} }
/* static struct snd_soc_codec *wm8900_codec;
* initialise the WM8900 driver
* register the mixer and dsp interfaces with the kernel static int wm8900_i2c_probe(struct i2c_client *i2c,
*/ const struct i2c_device_id *id)
static int wm8900_init(struct snd_soc_device *socdev)
{ {
struct snd_soc_codec *codec = socdev->codec; struct wm8900_priv *wm8900;
int ret = 0; struct snd_soc_codec *codec;
unsigned int reg; unsigned int reg;
struct i2c_client *i2c_client = socdev->codec->control_data; int ret;
wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
if (wm8900 == NULL)
return -ENOMEM;
codec = &wm8900->codec;
codec->private_data = wm8900;
codec->reg_cache = &wm8900->reg_cache[0];
codec->reg_cache_size = WM8900_MAXREG;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "WM8900"; codec->name = "WM8900";
codec->owner = THIS_MODULE; codec->owner = THIS_MODULE;
@ -1299,33 +1315,28 @@ static int wm8900_init(struct snd_soc_device *socdev)
codec->write = wm8900_write; codec->write = wm8900_write;
codec->dai = &wm8900_dai; codec->dai = &wm8900_dai;
codec->num_dai = 1; codec->num_dai = 1;
codec->reg_cache_size = WM8900_MAXREG; codec->hw_write = (hw_write_t)i2c_master_send;
codec->reg_cache = kmemdup(wm8900_reg_defaults, codec->control_data = i2c;
sizeof(wm8900_reg_defaults), GFP_KERNEL); codec->set_bias_level = wm8900_set_bias_level;
codec->dev = &i2c->dev;
if (codec->reg_cache == NULL)
return -ENOMEM;
reg = wm8900_read(codec, WM8900_REG_ID); reg = wm8900_read(codec, WM8900_REG_ID);
if (reg != 0x8900) { if (reg != 0x8900) {
dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n", dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
reg); ret = -ENODEV;
return -ENODEV; goto err;
}
codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
if (codec->private_data == NULL) {
ret = -ENOMEM;
goto priv_err;
} }
/* Read back from the chip */ /* Read back from the chip */
reg = wm8900_chip_read(codec, WM8900_REG_POWER1); reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
reg = (reg >> 12) & 0xf; reg = (reg >> 12) & 0xf;
dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg); dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
wm8900_reset(codec); wm8900_reset(codec);
/* Turn the chip on */
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the volume update bits */ /* Latch the volume update bits */
wm8900_write(codec, WM8900_REG_LINVOL, wm8900_write(codec, WM8900_REG_LINVOL,
wm8900_read(codec, WM8900_REG_LINVOL) | 0x100); wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
@ -1351,52 +1362,43 @@ static int wm8900_init(struct snd_soc_device *socdev)
/* Set the DAC and mixer output bias */ /* Set the DAC and mixer output bias */
wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81); wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
/* Register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
goto pcm_err;
}
/* Turn the chip on */
codec->bias_level = SND_SOC_BIAS_OFF;
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8900_add_controls(codec);
wm8900_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
priv_err:
kfree(codec->private_data);
return ret;
}
static struct i2c_client *wm8900_client;
static int wm8900_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
wm8900_client = i2c;
wm8900_dai.dev = &i2c->dev; wm8900_dai.dev = &i2c->dev;
return snd_soc_register_dai(&wm8900_dai);
wm8900_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
goto err;
}
ret = snd_soc_register_dai(&wm8900_dai);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
return ret;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8900);
wm8900_codec = NULL;
return ret;
} }
static int wm8900_i2c_remove(struct i2c_client *client) static int wm8900_i2c_remove(struct i2c_client *client)
{ {
snd_soc_unregister_dai(&wm8900_dai); snd_soc_unregister_dai(&wm8900_dai);
snd_soc_unregister_codec(wm8900_codec);
wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
wm8900_dai.dev = NULL; wm8900_dai.dev = NULL;
wm8900_client = NULL; kfree(wm8900_codec->private_data);
wm8900_codec = NULL;
return 0; return 0;
} }
@ -1408,7 +1410,7 @@ MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
static struct i2c_driver wm8900_i2c_driver = { static struct i2c_driver wm8900_i2c_driver = {
.driver = { .driver = {
.name = "WM8900 I2C codec", .name = "WM8900",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = wm8900_i2c_probe, .probe = wm8900_i2c_probe,
@ -1422,45 +1424,46 @@ static int wm8900_probe(struct platform_device *pdev)
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
int ret = 0; int ret = 0;
if (!wm8900_client) { if (!wm8900_codec) {
dev_err(&pdev->dev, "I2C client not yet instantiated\n"); dev_err(&pdev->dev, "I2C client not yet instantiated\n");
return -ENODEV; return -ENODEV;
} }
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); codec = wm8900_codec;
if (codec == NULL)
return -ENOMEM;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
socdev->codec = codec; socdev->codec = codec;
codec->set_bias_level = wm8900_set_bias_level; /* Register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register new PCMs\n");
goto pcm_err;
}
codec->hw_write = (hw_write_t)i2c_master_send; wm8900_add_controls(codec);
codec->control_data = wm8900_client; wm8900_add_widgets(codec);
ret = wm8900_init(socdev); ret = snd_soc_init_card(socdev);
if (ret != 0) if (ret < 0) {
kfree(codec); dev_err(&pdev->dev, "Failed to register card\n");
goto card_err;
}
return ret; return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
} }
/* power down chip */ /* power down chip */
static int wm8900_remove(struct platform_device *pdev) static int wm8900_remove(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev); snd_soc_dapm_free(socdev);
kfree(codec);
return 0; return 0;
} }

View File

@ -52,13 +52,6 @@
#define WM8900_DAC_CLKDIV_5_5 0x14 #define WM8900_DAC_CLKDIV_5_5 0x14
#define WM8900_DAC_CLKDIV_6 0x18 #define WM8900_DAC_CLKDIV_6 0x18
#define WM8900_
struct wm8900_setup_data {
int i2c_bus;
unsigned short i2c_address;
};
extern struct snd_soc_dai wm8900_dai; extern struct snd_soc_dai wm8900_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8900; extern struct snd_soc_codec_device soc_codec_dev_wm8900;