xref: /openbmc/linux/sound/soc/codecs/wm8728.c (revision 3e8e1952)
171cfc902SMark Brown /*
271cfc902SMark Brown  * wm8728.c  --  WM8728 ALSA SoC Audio driver
371cfc902SMark Brown  *
471cfc902SMark Brown  * Copyright 2008 Wolfson Microelectronics plc
571cfc902SMark Brown  *
671cfc902SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
771cfc902SMark Brown  *
871cfc902SMark Brown  * This program is free software; you can redistribute it and/or modify
971cfc902SMark Brown  * it under the terms of the GNU General Public License version 2 as
1071cfc902SMark Brown  * published by the Free Software Foundation.
1171cfc902SMark Brown  */
1271cfc902SMark Brown 
1371cfc902SMark Brown #include <linux/module.h>
1471cfc902SMark Brown #include <linux/moduleparam.h>
1571cfc902SMark Brown #include <linux/init.h>
1671cfc902SMark Brown #include <linux/delay.h>
1771cfc902SMark Brown #include <linux/pm.h>
1871cfc902SMark Brown #include <linux/i2c.h>
1971cfc902SMark Brown #include <linux/platform_device.h>
2071cfc902SMark Brown #include <linux/spi/spi.h>
2171cfc902SMark Brown #include <sound/core.h>
2271cfc902SMark Brown #include <sound/pcm.h>
2371cfc902SMark Brown #include <sound/pcm_params.h>
2471cfc902SMark Brown #include <sound/soc.h>
2571cfc902SMark Brown #include <sound/soc-dapm.h>
2671cfc902SMark Brown #include <sound/initval.h>
2771cfc902SMark Brown #include <sound/tlv.h>
2871cfc902SMark Brown 
2971cfc902SMark Brown #include "wm8728.h"
3071cfc902SMark Brown 
3171cfc902SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8728;
3271cfc902SMark Brown 
3371cfc902SMark Brown /*
3471cfc902SMark Brown  * We can't read the WM8728 register space so we cache them instead.
3571cfc902SMark Brown  * Note that the defaults here aren't the physical defaults, we latch
3671cfc902SMark Brown  * the volume update bits, mute the output and enable infinite zero
3771cfc902SMark Brown  * detect.
3871cfc902SMark Brown  */
3971cfc902SMark Brown static const u16 wm8728_reg_defaults[] = {
4071cfc902SMark Brown 	0x1ff,
4171cfc902SMark Brown 	0x1ff,
4271cfc902SMark Brown 	0x001,
4371cfc902SMark Brown 	0x100,
4471cfc902SMark Brown };
4571cfc902SMark Brown 
4671cfc902SMark Brown static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
4771cfc902SMark Brown 	unsigned int reg)
4871cfc902SMark Brown {
4971cfc902SMark Brown 	u16 *cache = codec->reg_cache;
5071cfc902SMark Brown 	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
5171cfc902SMark Brown 	return cache[reg];
5271cfc902SMark Brown }
5371cfc902SMark Brown 
5471cfc902SMark Brown static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
5571cfc902SMark Brown 	u16 reg, unsigned int value)
5671cfc902SMark Brown {
5771cfc902SMark Brown 	u16 *cache = codec->reg_cache;
5871cfc902SMark Brown 	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
5971cfc902SMark Brown 	cache[reg] = value;
6071cfc902SMark Brown }
6171cfc902SMark Brown 
6271cfc902SMark Brown /*
6371cfc902SMark Brown  * write to the WM8728 register space
6471cfc902SMark Brown  */
6571cfc902SMark Brown static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
6671cfc902SMark Brown 	unsigned int value)
6771cfc902SMark Brown {
6871cfc902SMark Brown 	u8 data[2];
6971cfc902SMark Brown 
7071cfc902SMark Brown 	/* data is
7171cfc902SMark Brown 	 *   D15..D9 WM8728 register offset
7271cfc902SMark Brown 	 *   D8...D0 register data
7371cfc902SMark Brown 	 */
7471cfc902SMark Brown 	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
7571cfc902SMark Brown 	data[1] = value & 0x00ff;
7671cfc902SMark Brown 
7771cfc902SMark Brown 	wm8728_write_reg_cache(codec, reg, value);
7871cfc902SMark Brown 
7971cfc902SMark Brown 	if (codec->hw_write(codec->control_data, data, 2) == 2)
8071cfc902SMark Brown 		return 0;
8171cfc902SMark Brown 	else
8271cfc902SMark Brown 		return -EIO;
8371cfc902SMark Brown }
8471cfc902SMark Brown 
8571cfc902SMark Brown static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
8671cfc902SMark Brown 
8771cfc902SMark Brown static const struct snd_kcontrol_new wm8728_snd_controls[] = {
8871cfc902SMark Brown 
8971cfc902SMark Brown SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
9071cfc902SMark Brown 		 0, 255, 0, wm8728_tlv),
9171cfc902SMark Brown 
9271cfc902SMark Brown SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
9371cfc902SMark Brown };
9471cfc902SMark Brown 
9571cfc902SMark Brown /*
9671cfc902SMark Brown  * DAPM controls.
9771cfc902SMark Brown  */
9871cfc902SMark Brown static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
9971cfc902SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
10071cfc902SMark Brown SND_SOC_DAPM_OUTPUT("VOUTL"),
10171cfc902SMark Brown SND_SOC_DAPM_OUTPUT("VOUTR"),
10271cfc902SMark Brown };
10371cfc902SMark Brown 
10471cfc902SMark Brown static const struct snd_soc_dapm_route intercon[] = {
10571cfc902SMark Brown 	{"VOUTL", NULL, "DAC"},
10671cfc902SMark Brown 	{"VOUTR", NULL, "DAC"},
10771cfc902SMark Brown };
10871cfc902SMark Brown 
10971cfc902SMark Brown static int wm8728_add_widgets(struct snd_soc_codec *codec)
11071cfc902SMark Brown {
11171cfc902SMark Brown 	snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
11271cfc902SMark Brown 				  ARRAY_SIZE(wm8728_dapm_widgets));
11371cfc902SMark Brown 
11471cfc902SMark Brown 	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
11571cfc902SMark Brown 
11671cfc902SMark Brown 	snd_soc_dapm_new_widgets(codec);
11771cfc902SMark Brown 
11871cfc902SMark Brown 	return 0;
11971cfc902SMark Brown }
12071cfc902SMark Brown 
12171cfc902SMark Brown static int wm8728_mute(struct snd_soc_dai *dai, int mute)
12271cfc902SMark Brown {
12371cfc902SMark Brown 	struct snd_soc_codec *codec = dai->codec;
12471cfc902SMark Brown 	u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
12571cfc902SMark Brown 
12671cfc902SMark Brown 	if (mute)
12771cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
12871cfc902SMark Brown 	else
12971cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
13071cfc902SMark Brown 
13171cfc902SMark Brown 	return 0;
13271cfc902SMark Brown }
13371cfc902SMark Brown 
13471cfc902SMark Brown static int wm8728_hw_params(struct snd_pcm_substream *substream,
135dee89c4dSMark Brown 	struct snd_pcm_hw_params *params,
136dee89c4dSMark Brown 	struct snd_soc_dai *dai)
13771cfc902SMark Brown {
13871cfc902SMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
13971cfc902SMark Brown 	struct snd_soc_device *socdev = rtd->socdev;
14071cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
14171cfc902SMark Brown 	u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
14271cfc902SMark Brown 
14371cfc902SMark Brown 	dac &= ~0x18;
14471cfc902SMark Brown 
14571cfc902SMark Brown 	switch (params_format(params)) {
14671cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
14771cfc902SMark Brown 		break;
14871cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
14971cfc902SMark Brown 		dac |= 0x10;
15071cfc902SMark Brown 		break;
15171cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
15271cfc902SMark Brown 		dac |= 0x08;
15371cfc902SMark Brown 		break;
15471cfc902SMark Brown 	default:
15571cfc902SMark Brown 		return -EINVAL;
15671cfc902SMark Brown 	}
15771cfc902SMark Brown 
15871cfc902SMark Brown 	wm8728_write(codec, WM8728_DACCTL, dac);
15971cfc902SMark Brown 
16071cfc902SMark Brown 	return 0;
16171cfc902SMark Brown }
16271cfc902SMark Brown 
16371cfc902SMark Brown static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
16471cfc902SMark Brown 		unsigned int fmt)
16571cfc902SMark Brown {
16671cfc902SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
16771cfc902SMark Brown 	u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
16871cfc902SMark Brown 
16971cfc902SMark Brown 	/* Currently only I2S is supported by the driver, though the
17071cfc902SMark Brown 	 * hardware is more flexible.
17171cfc902SMark Brown 	 */
17271cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
17371cfc902SMark Brown 	case SND_SOC_DAIFMT_I2S:
17471cfc902SMark Brown 		iface |= 1;
17571cfc902SMark Brown 		break;
17671cfc902SMark Brown 	default:
17771cfc902SMark Brown 		return -EINVAL;
17871cfc902SMark Brown 	}
17971cfc902SMark Brown 
18071cfc902SMark Brown 	/* The hardware only support full slave mode */
18171cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
18271cfc902SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
18371cfc902SMark Brown 		break;
18471cfc902SMark Brown 	default:
18571cfc902SMark Brown 		return -EINVAL;
18671cfc902SMark Brown 	}
18771cfc902SMark Brown 
18871cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
18971cfc902SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
19071cfc902SMark Brown 		iface &= ~0x22;
19171cfc902SMark Brown 		break;
19271cfc902SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
19371cfc902SMark Brown 		iface |=  0x20;
19471cfc902SMark Brown 		iface &= ~0x02;
19571cfc902SMark Brown 		break;
19671cfc902SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
19771cfc902SMark Brown 		iface |= 0x02;
19871cfc902SMark Brown 		iface &= ~0x20;
19971cfc902SMark Brown 		break;
20071cfc902SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
20171cfc902SMark Brown 		iface |= 0x22;
20271cfc902SMark Brown 		break;
20371cfc902SMark Brown 	default:
20471cfc902SMark Brown 		return -EINVAL;
20571cfc902SMark Brown 	}
20671cfc902SMark Brown 
20771cfc902SMark Brown 	wm8728_write(codec, WM8728_IFCTL, iface);
20871cfc902SMark Brown 	return 0;
20971cfc902SMark Brown }
21071cfc902SMark Brown 
21171cfc902SMark Brown static int wm8728_set_bias_level(struct snd_soc_codec *codec,
21271cfc902SMark Brown 				 enum snd_soc_bias_level level)
21371cfc902SMark Brown {
21471cfc902SMark Brown 	u16 reg;
21571cfc902SMark Brown 	int i;
21671cfc902SMark Brown 
21771cfc902SMark Brown 	switch (level) {
21871cfc902SMark Brown 	case SND_SOC_BIAS_ON:
21971cfc902SMark Brown 	case SND_SOC_BIAS_PREPARE:
22071cfc902SMark Brown 	case SND_SOC_BIAS_STANDBY:
22171cfc902SMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
22271cfc902SMark Brown 			/* Power everything up... */
22371cfc902SMark Brown 			reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
22471cfc902SMark Brown 			wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
22571cfc902SMark Brown 
22671cfc902SMark Brown 			/* ..then sync in the register cache. */
22771cfc902SMark Brown 			for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
22871cfc902SMark Brown 				wm8728_write(codec, i,
22971cfc902SMark Brown 					     wm8728_read_reg_cache(codec, i));
23071cfc902SMark Brown 		}
23171cfc902SMark Brown 		break;
23271cfc902SMark Brown 
23371cfc902SMark Brown 	case SND_SOC_BIAS_OFF:
23471cfc902SMark Brown 		reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
23571cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
23671cfc902SMark Brown 		break;
23771cfc902SMark Brown 	}
23871cfc902SMark Brown 	codec->bias_level = level;
23971cfc902SMark Brown 	return 0;
24071cfc902SMark Brown }
24171cfc902SMark Brown 
24271cfc902SMark Brown #define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
24371cfc902SMark Brown 
24471cfc902SMark Brown #define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
24571cfc902SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
24671cfc902SMark Brown 
24771cfc902SMark Brown struct snd_soc_dai wm8728_dai = {
24871cfc902SMark Brown 	.name = "WM8728",
24971cfc902SMark Brown 	.playback = {
25071cfc902SMark Brown 		.stream_name = "Playback",
25171cfc902SMark Brown 		.channels_min = 2,
25271cfc902SMark Brown 		.channels_max = 2,
25371cfc902SMark Brown 		.rates = WM8728_RATES,
25471cfc902SMark Brown 		.formats = WM8728_FORMATS,
25571cfc902SMark Brown 	},
25671cfc902SMark Brown 	.ops = {
25771cfc902SMark Brown 		 .hw_params = wm8728_hw_params,
25871cfc902SMark Brown 		 .digital_mute = wm8728_mute,
25971cfc902SMark Brown 		 .set_fmt = wm8728_set_dai_fmt,
26071cfc902SMark Brown 	}
26171cfc902SMark Brown };
26271cfc902SMark Brown EXPORT_SYMBOL_GPL(wm8728_dai);
26371cfc902SMark Brown 
26471cfc902SMark Brown static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
26571cfc902SMark Brown {
26671cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
26771cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
26871cfc902SMark Brown 
26971cfc902SMark Brown 	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
27071cfc902SMark Brown 
27171cfc902SMark Brown 	return 0;
27271cfc902SMark Brown }
27371cfc902SMark Brown 
27471cfc902SMark Brown static int wm8728_resume(struct platform_device *pdev)
27571cfc902SMark Brown {
27671cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
27771cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
27871cfc902SMark Brown 
27971cfc902SMark Brown 	wm8728_set_bias_level(codec, codec->suspend_bias_level);
28071cfc902SMark Brown 
28171cfc902SMark Brown 	return 0;
28271cfc902SMark Brown }
28371cfc902SMark Brown 
28471cfc902SMark Brown /*
28571cfc902SMark Brown  * initialise the WM8728 driver
28671cfc902SMark Brown  * register the mixer and dsp interfaces with the kernel
28771cfc902SMark Brown  */
28871cfc902SMark Brown static int wm8728_init(struct snd_soc_device *socdev)
28971cfc902SMark Brown {
29071cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
29171cfc902SMark Brown 	int ret = 0;
29271cfc902SMark Brown 
29371cfc902SMark Brown 	codec->name = "WM8728";
29471cfc902SMark Brown 	codec->owner = THIS_MODULE;
29571cfc902SMark Brown 	codec->read = wm8728_read_reg_cache;
29671cfc902SMark Brown 	codec->write = wm8728_write;
29771cfc902SMark Brown 	codec->set_bias_level = wm8728_set_bias_level;
29871cfc902SMark Brown 	codec->dai = &wm8728_dai;
29971cfc902SMark Brown 	codec->num_dai = 1;
30071cfc902SMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
30171cfc902SMark Brown 	codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
30271cfc902SMark Brown 	codec->reg_cache = kmemdup(wm8728_reg_defaults,
30371cfc902SMark Brown 				   sizeof(wm8728_reg_defaults),
30471cfc902SMark Brown 				   GFP_KERNEL);
30571cfc902SMark Brown 	if (codec->reg_cache == NULL)
30671cfc902SMark Brown 		return -ENOMEM;
30771cfc902SMark Brown 
30871cfc902SMark Brown 	/* register pcms */
30971cfc902SMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
31071cfc902SMark Brown 	if (ret < 0) {
31171cfc902SMark Brown 		printk(KERN_ERR "wm8728: failed to create pcms\n");
31271cfc902SMark Brown 		goto pcm_err;
31371cfc902SMark Brown 	}
31471cfc902SMark Brown 
31571cfc902SMark Brown 	/* power on device */
31671cfc902SMark Brown 	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
31771cfc902SMark Brown 
3183e8e1952SIan Molton 	snd_soc_add_controls(codec, wm8728_snd_controls,
3193e8e1952SIan Molton 				ARRAY_SIZE(wm8728_snd_controls));
32071cfc902SMark Brown 	wm8728_add_widgets(codec);
321968a6025SMark Brown 	ret = snd_soc_init_card(socdev);
32271cfc902SMark Brown 	if (ret < 0) {
32371cfc902SMark Brown 		printk(KERN_ERR "wm8728: failed to register card\n");
32471cfc902SMark Brown 		goto card_err;
32571cfc902SMark Brown 	}
32671cfc902SMark Brown 
32771cfc902SMark Brown 	return ret;
32871cfc902SMark Brown 
32971cfc902SMark Brown card_err:
33071cfc902SMark Brown 	snd_soc_free_pcms(socdev);
33171cfc902SMark Brown 	snd_soc_dapm_free(socdev);
33271cfc902SMark Brown pcm_err:
33371cfc902SMark Brown 	kfree(codec->reg_cache);
33471cfc902SMark Brown 	return ret;
33571cfc902SMark Brown }
33671cfc902SMark Brown 
33771cfc902SMark Brown static struct snd_soc_device *wm8728_socdev;
33871cfc902SMark Brown 
33971cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
34071cfc902SMark Brown 
34171cfc902SMark Brown /*
34271cfc902SMark Brown  * WM8728 2 wire address is determined by GPIO5
34371cfc902SMark Brown  * state during powerup.
34471cfc902SMark Brown  *    low  = 0x1a
34571cfc902SMark Brown  *    high = 0x1b
34671cfc902SMark Brown  */
34771cfc902SMark Brown 
34871cfc902SMark Brown static int wm8728_i2c_probe(struct i2c_client *i2c,
34971cfc902SMark Brown 			    const struct i2c_device_id *id)
35071cfc902SMark Brown {
35171cfc902SMark Brown 	struct snd_soc_device *socdev = wm8728_socdev;
35271cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
35371cfc902SMark Brown 	int ret;
35471cfc902SMark Brown 
35571cfc902SMark Brown 	i2c_set_clientdata(i2c, codec);
35671cfc902SMark Brown 	codec->control_data = i2c;
35771cfc902SMark Brown 
35871cfc902SMark Brown 	ret = wm8728_init(socdev);
35971cfc902SMark Brown 	if (ret < 0)
36071cfc902SMark Brown 		pr_err("failed to initialise WM8728\n");
36171cfc902SMark Brown 
36271cfc902SMark Brown 	return ret;
36371cfc902SMark Brown }
36471cfc902SMark Brown 
36571cfc902SMark Brown static int wm8728_i2c_remove(struct i2c_client *client)
36671cfc902SMark Brown {
36771cfc902SMark Brown 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
36871cfc902SMark Brown 	kfree(codec->reg_cache);
36971cfc902SMark Brown 	return 0;
37071cfc902SMark Brown }
37171cfc902SMark Brown 
37271cfc902SMark Brown static const struct i2c_device_id wm8728_i2c_id[] = {
37371cfc902SMark Brown 	{ "wm8728", 0 },
37471cfc902SMark Brown 	{ }
37571cfc902SMark Brown };
37671cfc902SMark Brown MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
37771cfc902SMark Brown 
37871cfc902SMark Brown static struct i2c_driver wm8728_i2c_driver = {
37971cfc902SMark Brown 	.driver = {
38071cfc902SMark Brown 		.name = "WM8728 I2C Codec",
38171cfc902SMark Brown 		.owner = THIS_MODULE,
38271cfc902SMark Brown 	},
38371cfc902SMark Brown 	.probe =    wm8728_i2c_probe,
38471cfc902SMark Brown 	.remove =   wm8728_i2c_remove,
38571cfc902SMark Brown 	.id_table = wm8728_i2c_id,
38671cfc902SMark Brown };
38771cfc902SMark Brown 
38871cfc902SMark Brown static int wm8728_add_i2c_device(struct platform_device *pdev,
38971cfc902SMark Brown 				 const struct wm8728_setup_data *setup)
39071cfc902SMark Brown {
39171cfc902SMark Brown 	struct i2c_board_info info;
39271cfc902SMark Brown 	struct i2c_adapter *adapter;
39371cfc902SMark Brown 	struct i2c_client *client;
39471cfc902SMark Brown 	int ret;
39571cfc902SMark Brown 
39671cfc902SMark Brown 	ret = i2c_add_driver(&wm8728_i2c_driver);
39771cfc902SMark Brown 	if (ret != 0) {
39871cfc902SMark Brown 		dev_err(&pdev->dev, "can't add i2c driver\n");
39971cfc902SMark Brown 		return ret;
40071cfc902SMark Brown 	}
40171cfc902SMark Brown 
40271cfc902SMark Brown 	memset(&info, 0, sizeof(struct i2c_board_info));
40371cfc902SMark Brown 	info.addr = setup->i2c_address;
40471cfc902SMark Brown 	strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
40571cfc902SMark Brown 
40671cfc902SMark Brown 	adapter = i2c_get_adapter(setup->i2c_bus);
40771cfc902SMark Brown 	if (!adapter) {
40871cfc902SMark Brown 		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
40971cfc902SMark Brown 			setup->i2c_bus);
41071cfc902SMark Brown 		goto err_driver;
41171cfc902SMark Brown 	}
41271cfc902SMark Brown 
41371cfc902SMark Brown 	client = i2c_new_device(adapter, &info);
41471cfc902SMark Brown 	i2c_put_adapter(adapter);
41571cfc902SMark Brown 	if (!client) {
41671cfc902SMark Brown 		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
41771cfc902SMark Brown 			(unsigned int)info.addr);
41871cfc902SMark Brown 		goto err_driver;
41971cfc902SMark Brown 	}
42071cfc902SMark Brown 
42171cfc902SMark Brown 	return 0;
42271cfc902SMark Brown 
42371cfc902SMark Brown err_driver:
42471cfc902SMark Brown 	i2c_del_driver(&wm8728_i2c_driver);
42571cfc902SMark Brown 	return -ENODEV;
42671cfc902SMark Brown }
42771cfc902SMark Brown #endif
42871cfc902SMark Brown 
42971cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
43071cfc902SMark Brown static int __devinit wm8728_spi_probe(struct spi_device *spi)
43171cfc902SMark Brown {
43271cfc902SMark Brown 	struct snd_soc_device *socdev = wm8728_socdev;
43371cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
43471cfc902SMark Brown 	int ret;
43571cfc902SMark Brown 
43671cfc902SMark Brown 	codec->control_data = spi;
43771cfc902SMark Brown 
43871cfc902SMark Brown 	ret = wm8728_init(socdev);
43971cfc902SMark Brown 	if (ret < 0)
44071cfc902SMark Brown 		dev_err(&spi->dev, "failed to initialise WM8728\n");
44171cfc902SMark Brown 
44271cfc902SMark Brown 	return ret;
44371cfc902SMark Brown }
44471cfc902SMark Brown 
44571cfc902SMark Brown static int __devexit wm8728_spi_remove(struct spi_device *spi)
44671cfc902SMark Brown {
44771cfc902SMark Brown 	return 0;
44871cfc902SMark Brown }
44971cfc902SMark Brown 
45071cfc902SMark Brown static struct spi_driver wm8728_spi_driver = {
45171cfc902SMark Brown 	.driver = {
45271cfc902SMark Brown 		.name	= "wm8728",
45371cfc902SMark Brown 		.bus	= &spi_bus_type,
45471cfc902SMark Brown 		.owner	= THIS_MODULE,
45571cfc902SMark Brown 	},
45671cfc902SMark Brown 	.probe		= wm8728_spi_probe,
45771cfc902SMark Brown 	.remove		= __devexit_p(wm8728_spi_remove),
45871cfc902SMark Brown };
45971cfc902SMark Brown 
46071cfc902SMark Brown static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
46171cfc902SMark Brown {
46271cfc902SMark Brown 	struct spi_transfer t;
46371cfc902SMark Brown 	struct spi_message m;
46471cfc902SMark Brown 	u8 msg[2];
46571cfc902SMark Brown 
46671cfc902SMark Brown 	if (len <= 0)
46771cfc902SMark Brown 		return 0;
46871cfc902SMark Brown 
46971cfc902SMark Brown 	msg[0] = data[0];
47071cfc902SMark Brown 	msg[1] = data[1];
47171cfc902SMark Brown 
47271cfc902SMark Brown 	spi_message_init(&m);
47371cfc902SMark Brown 	memset(&t, 0, (sizeof t));
47471cfc902SMark Brown 
47571cfc902SMark Brown 	t.tx_buf = &msg[0];
47671cfc902SMark Brown 	t.len = len;
47771cfc902SMark Brown 
47871cfc902SMark Brown 	spi_message_add_tail(&t, &m);
47971cfc902SMark Brown 	spi_sync(spi, &m);
48071cfc902SMark Brown 
48171cfc902SMark Brown 	return len;
48271cfc902SMark Brown }
48371cfc902SMark Brown #endif /* CONFIG_SPI_MASTER */
48471cfc902SMark Brown 
48571cfc902SMark Brown static int wm8728_probe(struct platform_device *pdev)
48671cfc902SMark Brown {
48771cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
48871cfc902SMark Brown 	struct wm8728_setup_data *setup;
48971cfc902SMark Brown 	struct snd_soc_codec *codec;
49071cfc902SMark Brown 	int ret = 0;
49171cfc902SMark Brown 
49271cfc902SMark Brown 	setup = socdev->codec_data;
49371cfc902SMark Brown 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
49471cfc902SMark Brown 	if (codec == NULL)
49571cfc902SMark Brown 		return -ENOMEM;
49671cfc902SMark Brown 
49771cfc902SMark Brown 	socdev->codec = codec;
49871cfc902SMark Brown 	mutex_init(&codec->mutex);
49971cfc902SMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
50071cfc902SMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
50171cfc902SMark Brown 
50271cfc902SMark Brown 	wm8728_socdev = socdev;
50371cfc902SMark Brown 	ret = -ENODEV;
50471cfc902SMark Brown 
50571cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
50671cfc902SMark Brown 	if (setup->i2c_address) {
50771cfc902SMark Brown 		codec->hw_write = (hw_write_t)i2c_master_send;
50871cfc902SMark Brown 		ret = wm8728_add_i2c_device(pdev, setup);
50971cfc902SMark Brown 	}
51071cfc902SMark Brown #endif
51171cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
51271cfc902SMark Brown 	if (setup->spi) {
51371cfc902SMark Brown 		codec->hw_write = (hw_write_t)wm8728_spi_write;
51471cfc902SMark Brown 		ret = spi_register_driver(&wm8728_spi_driver);
51571cfc902SMark Brown 		if (ret != 0)
51671cfc902SMark Brown 			printk(KERN_ERR "can't add spi driver");
51771cfc902SMark Brown 	}
51871cfc902SMark Brown #endif
51971cfc902SMark Brown 
52071cfc902SMark Brown 	if (ret != 0)
52171cfc902SMark Brown 		kfree(codec);
52271cfc902SMark Brown 
52371cfc902SMark Brown 	return ret;
52471cfc902SMark Brown }
52571cfc902SMark Brown 
52671cfc902SMark Brown /* power down chip */
52771cfc902SMark Brown static int wm8728_remove(struct platform_device *pdev)
52871cfc902SMark Brown {
52971cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
53071cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
53171cfc902SMark Brown 
53271cfc902SMark Brown 	if (codec->control_data)
53371cfc902SMark Brown 		wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
53471cfc902SMark Brown 
53571cfc902SMark Brown 	snd_soc_free_pcms(socdev);
53671cfc902SMark Brown 	snd_soc_dapm_free(socdev);
53771cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
53871cfc902SMark Brown 	i2c_unregister_device(codec->control_data);
53971cfc902SMark Brown 	i2c_del_driver(&wm8728_i2c_driver);
54071cfc902SMark Brown #endif
54171cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
54271cfc902SMark Brown 	spi_unregister_driver(&wm8728_spi_driver);
54371cfc902SMark Brown #endif
54471cfc902SMark Brown 	kfree(codec);
54571cfc902SMark Brown 
54671cfc902SMark Brown 	return 0;
54771cfc902SMark Brown }
54871cfc902SMark Brown 
54971cfc902SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8728 = {
55071cfc902SMark Brown 	.probe = 	wm8728_probe,
55171cfc902SMark Brown 	.remove = 	wm8728_remove,
55271cfc902SMark Brown 	.suspend = 	wm8728_suspend,
55371cfc902SMark Brown 	.resume =	wm8728_resume,
55471cfc902SMark Brown };
55571cfc902SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
55671cfc902SMark Brown 
557c9b3a40fSTakashi Iwai static int __init wm8728_modinit(void)
55864089b84SMark Brown {
55964089b84SMark Brown 	return snd_soc_register_dai(&wm8728_dai);
56064089b84SMark Brown }
56164089b84SMark Brown module_init(wm8728_modinit);
56264089b84SMark Brown 
56364089b84SMark Brown static void __exit wm8728_exit(void)
56464089b84SMark Brown {
56564089b84SMark Brown 	snd_soc_unregister_dai(&wm8728_dai);
56664089b84SMark Brown }
56764089b84SMark Brown module_exit(wm8728_exit);
56864089b84SMark Brown 
56971cfc902SMark Brown MODULE_DESCRIPTION("ASoC WM8728 driver");
57071cfc902SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
57171cfc902SMark Brown MODULE_LICENSE("GPL");
572