xref: /openbmc/linux/sound/soc/codecs/wm8728.c (revision c9b3a40f)
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 static int wm8728_add_controls(struct snd_soc_codec *codec)
9671cfc902SMark Brown {
9771cfc902SMark Brown 	int err, i;
9871cfc902SMark Brown 
9971cfc902SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
10071cfc902SMark Brown 		err = snd_ctl_add(codec->card,
10171cfc902SMark Brown 				  snd_soc_cnew(&wm8728_snd_controls[i],
10271cfc902SMark Brown 						codec, NULL));
10371cfc902SMark Brown 		if (err < 0)
10471cfc902SMark Brown 			return err;
10571cfc902SMark Brown 	}
10671cfc902SMark Brown 
10771cfc902SMark Brown 	return 0;
10871cfc902SMark Brown }
10971cfc902SMark Brown 
11071cfc902SMark Brown /*
11171cfc902SMark Brown  * DAPM controls.
11271cfc902SMark Brown  */
11371cfc902SMark Brown static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
11471cfc902SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
11571cfc902SMark Brown SND_SOC_DAPM_OUTPUT("VOUTL"),
11671cfc902SMark Brown SND_SOC_DAPM_OUTPUT("VOUTR"),
11771cfc902SMark Brown };
11871cfc902SMark Brown 
11971cfc902SMark Brown static const struct snd_soc_dapm_route intercon[] = {
12071cfc902SMark Brown 	{"VOUTL", NULL, "DAC"},
12171cfc902SMark Brown 	{"VOUTR", NULL, "DAC"},
12271cfc902SMark Brown };
12371cfc902SMark Brown 
12471cfc902SMark Brown static int wm8728_add_widgets(struct snd_soc_codec *codec)
12571cfc902SMark Brown {
12671cfc902SMark Brown 	snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
12771cfc902SMark Brown 				  ARRAY_SIZE(wm8728_dapm_widgets));
12871cfc902SMark Brown 
12971cfc902SMark Brown 	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
13071cfc902SMark Brown 
13171cfc902SMark Brown 	snd_soc_dapm_new_widgets(codec);
13271cfc902SMark Brown 
13371cfc902SMark Brown 	return 0;
13471cfc902SMark Brown }
13571cfc902SMark Brown 
13671cfc902SMark Brown static int wm8728_mute(struct snd_soc_dai *dai, int mute)
13771cfc902SMark Brown {
13871cfc902SMark Brown 	struct snd_soc_codec *codec = dai->codec;
13971cfc902SMark Brown 	u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
14071cfc902SMark Brown 
14171cfc902SMark Brown 	if (mute)
14271cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
14371cfc902SMark Brown 	else
14471cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
14571cfc902SMark Brown 
14671cfc902SMark Brown 	return 0;
14771cfc902SMark Brown }
14871cfc902SMark Brown 
14971cfc902SMark Brown static int wm8728_hw_params(struct snd_pcm_substream *substream,
150dee89c4dSMark Brown 	struct snd_pcm_hw_params *params,
151dee89c4dSMark Brown 	struct snd_soc_dai *dai)
15271cfc902SMark Brown {
15371cfc902SMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
15471cfc902SMark Brown 	struct snd_soc_device *socdev = rtd->socdev;
15571cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
15671cfc902SMark Brown 	u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
15771cfc902SMark Brown 
15871cfc902SMark Brown 	dac &= ~0x18;
15971cfc902SMark Brown 
16071cfc902SMark Brown 	switch (params_format(params)) {
16171cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
16271cfc902SMark Brown 		break;
16371cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
16471cfc902SMark Brown 		dac |= 0x10;
16571cfc902SMark Brown 		break;
16671cfc902SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
16771cfc902SMark Brown 		dac |= 0x08;
16871cfc902SMark Brown 		break;
16971cfc902SMark Brown 	default:
17071cfc902SMark Brown 		return -EINVAL;
17171cfc902SMark Brown 	}
17271cfc902SMark Brown 
17371cfc902SMark Brown 	wm8728_write(codec, WM8728_DACCTL, dac);
17471cfc902SMark Brown 
17571cfc902SMark Brown 	return 0;
17671cfc902SMark Brown }
17771cfc902SMark Brown 
17871cfc902SMark Brown static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
17971cfc902SMark Brown 		unsigned int fmt)
18071cfc902SMark Brown {
18171cfc902SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
18271cfc902SMark Brown 	u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
18371cfc902SMark Brown 
18471cfc902SMark Brown 	/* Currently only I2S is supported by the driver, though the
18571cfc902SMark Brown 	 * hardware is more flexible.
18671cfc902SMark Brown 	 */
18771cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
18871cfc902SMark Brown 	case SND_SOC_DAIFMT_I2S:
18971cfc902SMark Brown 		iface |= 1;
19071cfc902SMark Brown 		break;
19171cfc902SMark Brown 	default:
19271cfc902SMark Brown 		return -EINVAL;
19371cfc902SMark Brown 	}
19471cfc902SMark Brown 
19571cfc902SMark Brown 	/* The hardware only support full slave mode */
19671cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
19771cfc902SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
19871cfc902SMark Brown 		break;
19971cfc902SMark Brown 	default:
20071cfc902SMark Brown 		return -EINVAL;
20171cfc902SMark Brown 	}
20271cfc902SMark Brown 
20371cfc902SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
20471cfc902SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
20571cfc902SMark Brown 		iface &= ~0x22;
20671cfc902SMark Brown 		break;
20771cfc902SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
20871cfc902SMark Brown 		iface |=  0x20;
20971cfc902SMark Brown 		iface &= ~0x02;
21071cfc902SMark Brown 		break;
21171cfc902SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
21271cfc902SMark Brown 		iface |= 0x02;
21371cfc902SMark Brown 		iface &= ~0x20;
21471cfc902SMark Brown 		break;
21571cfc902SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
21671cfc902SMark Brown 		iface |= 0x22;
21771cfc902SMark Brown 		break;
21871cfc902SMark Brown 	default:
21971cfc902SMark Brown 		return -EINVAL;
22071cfc902SMark Brown 	}
22171cfc902SMark Brown 
22271cfc902SMark Brown 	wm8728_write(codec, WM8728_IFCTL, iface);
22371cfc902SMark Brown 	return 0;
22471cfc902SMark Brown }
22571cfc902SMark Brown 
22671cfc902SMark Brown static int wm8728_set_bias_level(struct snd_soc_codec *codec,
22771cfc902SMark Brown 				 enum snd_soc_bias_level level)
22871cfc902SMark Brown {
22971cfc902SMark Brown 	u16 reg;
23071cfc902SMark Brown 	int i;
23171cfc902SMark Brown 
23271cfc902SMark Brown 	switch (level) {
23371cfc902SMark Brown 	case SND_SOC_BIAS_ON:
23471cfc902SMark Brown 	case SND_SOC_BIAS_PREPARE:
23571cfc902SMark Brown 	case SND_SOC_BIAS_STANDBY:
23671cfc902SMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
23771cfc902SMark Brown 			/* Power everything up... */
23871cfc902SMark Brown 			reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
23971cfc902SMark Brown 			wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
24071cfc902SMark Brown 
24171cfc902SMark Brown 			/* ..then sync in the register cache. */
24271cfc902SMark Brown 			for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
24371cfc902SMark Brown 				wm8728_write(codec, i,
24471cfc902SMark Brown 					     wm8728_read_reg_cache(codec, i));
24571cfc902SMark Brown 		}
24671cfc902SMark Brown 		break;
24771cfc902SMark Brown 
24871cfc902SMark Brown 	case SND_SOC_BIAS_OFF:
24971cfc902SMark Brown 		reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
25071cfc902SMark Brown 		wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
25171cfc902SMark Brown 		break;
25271cfc902SMark Brown 	}
25371cfc902SMark Brown 	codec->bias_level = level;
25471cfc902SMark Brown 	return 0;
25571cfc902SMark Brown }
25671cfc902SMark Brown 
25771cfc902SMark Brown #define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
25871cfc902SMark Brown 
25971cfc902SMark Brown #define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
26071cfc902SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
26171cfc902SMark Brown 
26271cfc902SMark Brown struct snd_soc_dai wm8728_dai = {
26371cfc902SMark Brown 	.name = "WM8728",
26471cfc902SMark Brown 	.playback = {
26571cfc902SMark Brown 		.stream_name = "Playback",
26671cfc902SMark Brown 		.channels_min = 2,
26771cfc902SMark Brown 		.channels_max = 2,
26871cfc902SMark Brown 		.rates = WM8728_RATES,
26971cfc902SMark Brown 		.formats = WM8728_FORMATS,
27071cfc902SMark Brown 	},
27171cfc902SMark Brown 	.ops = {
27271cfc902SMark Brown 		 .hw_params = wm8728_hw_params,
27371cfc902SMark Brown 		 .digital_mute = wm8728_mute,
27471cfc902SMark Brown 		 .set_fmt = wm8728_set_dai_fmt,
27571cfc902SMark Brown 	}
27671cfc902SMark Brown };
27771cfc902SMark Brown EXPORT_SYMBOL_GPL(wm8728_dai);
27871cfc902SMark Brown 
27971cfc902SMark Brown static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
28071cfc902SMark Brown {
28171cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
28271cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
28371cfc902SMark Brown 
28471cfc902SMark Brown 	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
28571cfc902SMark Brown 
28671cfc902SMark Brown 	return 0;
28771cfc902SMark Brown }
28871cfc902SMark Brown 
28971cfc902SMark Brown static int wm8728_resume(struct platform_device *pdev)
29071cfc902SMark Brown {
29171cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
29271cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
29371cfc902SMark Brown 
29471cfc902SMark Brown 	wm8728_set_bias_level(codec, codec->suspend_bias_level);
29571cfc902SMark Brown 
29671cfc902SMark Brown 	return 0;
29771cfc902SMark Brown }
29871cfc902SMark Brown 
29971cfc902SMark Brown /*
30071cfc902SMark Brown  * initialise the WM8728 driver
30171cfc902SMark Brown  * register the mixer and dsp interfaces with the kernel
30271cfc902SMark Brown  */
30371cfc902SMark Brown static int wm8728_init(struct snd_soc_device *socdev)
30471cfc902SMark Brown {
30571cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
30671cfc902SMark Brown 	int ret = 0;
30771cfc902SMark Brown 
30871cfc902SMark Brown 	codec->name = "WM8728";
30971cfc902SMark Brown 	codec->owner = THIS_MODULE;
31071cfc902SMark Brown 	codec->read = wm8728_read_reg_cache;
31171cfc902SMark Brown 	codec->write = wm8728_write;
31271cfc902SMark Brown 	codec->set_bias_level = wm8728_set_bias_level;
31371cfc902SMark Brown 	codec->dai = &wm8728_dai;
31471cfc902SMark Brown 	codec->num_dai = 1;
31571cfc902SMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
31671cfc902SMark Brown 	codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
31771cfc902SMark Brown 	codec->reg_cache = kmemdup(wm8728_reg_defaults,
31871cfc902SMark Brown 				   sizeof(wm8728_reg_defaults),
31971cfc902SMark Brown 				   GFP_KERNEL);
32071cfc902SMark Brown 	if (codec->reg_cache == NULL)
32171cfc902SMark Brown 		return -ENOMEM;
32271cfc902SMark Brown 
32371cfc902SMark Brown 	/* register pcms */
32471cfc902SMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
32571cfc902SMark Brown 	if (ret < 0) {
32671cfc902SMark Brown 		printk(KERN_ERR "wm8728: failed to create pcms\n");
32771cfc902SMark Brown 		goto pcm_err;
32871cfc902SMark Brown 	}
32971cfc902SMark Brown 
33071cfc902SMark Brown 	/* power on device */
33171cfc902SMark Brown 	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
33271cfc902SMark Brown 
33371cfc902SMark Brown 	wm8728_add_controls(codec);
33471cfc902SMark Brown 	wm8728_add_widgets(codec);
335968a6025SMark Brown 	ret = snd_soc_init_card(socdev);
33671cfc902SMark Brown 	if (ret < 0) {
33771cfc902SMark Brown 		printk(KERN_ERR "wm8728: failed to register card\n");
33871cfc902SMark Brown 		goto card_err;
33971cfc902SMark Brown 	}
34071cfc902SMark Brown 
34171cfc902SMark Brown 	return ret;
34271cfc902SMark Brown 
34371cfc902SMark Brown card_err:
34471cfc902SMark Brown 	snd_soc_free_pcms(socdev);
34571cfc902SMark Brown 	snd_soc_dapm_free(socdev);
34671cfc902SMark Brown pcm_err:
34771cfc902SMark Brown 	kfree(codec->reg_cache);
34871cfc902SMark Brown 	return ret;
34971cfc902SMark Brown }
35071cfc902SMark Brown 
35171cfc902SMark Brown static struct snd_soc_device *wm8728_socdev;
35271cfc902SMark Brown 
35371cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
35471cfc902SMark Brown 
35571cfc902SMark Brown /*
35671cfc902SMark Brown  * WM8728 2 wire address is determined by GPIO5
35771cfc902SMark Brown  * state during powerup.
35871cfc902SMark Brown  *    low  = 0x1a
35971cfc902SMark Brown  *    high = 0x1b
36071cfc902SMark Brown  */
36171cfc902SMark Brown 
36271cfc902SMark Brown static int wm8728_i2c_probe(struct i2c_client *i2c,
36371cfc902SMark Brown 			    const struct i2c_device_id *id)
36471cfc902SMark Brown {
36571cfc902SMark Brown 	struct snd_soc_device *socdev = wm8728_socdev;
36671cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
36771cfc902SMark Brown 	int ret;
36871cfc902SMark Brown 
36971cfc902SMark Brown 	i2c_set_clientdata(i2c, codec);
37071cfc902SMark Brown 	codec->control_data = i2c;
37171cfc902SMark Brown 
37271cfc902SMark Brown 	ret = wm8728_init(socdev);
37371cfc902SMark Brown 	if (ret < 0)
37471cfc902SMark Brown 		pr_err("failed to initialise WM8728\n");
37571cfc902SMark Brown 
37671cfc902SMark Brown 	return ret;
37771cfc902SMark Brown }
37871cfc902SMark Brown 
37971cfc902SMark Brown static int wm8728_i2c_remove(struct i2c_client *client)
38071cfc902SMark Brown {
38171cfc902SMark Brown 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
38271cfc902SMark Brown 	kfree(codec->reg_cache);
38371cfc902SMark Brown 	return 0;
38471cfc902SMark Brown }
38571cfc902SMark Brown 
38671cfc902SMark Brown static const struct i2c_device_id wm8728_i2c_id[] = {
38771cfc902SMark Brown 	{ "wm8728", 0 },
38871cfc902SMark Brown 	{ }
38971cfc902SMark Brown };
39071cfc902SMark Brown MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
39171cfc902SMark Brown 
39271cfc902SMark Brown static struct i2c_driver wm8728_i2c_driver = {
39371cfc902SMark Brown 	.driver = {
39471cfc902SMark Brown 		.name = "WM8728 I2C Codec",
39571cfc902SMark Brown 		.owner = THIS_MODULE,
39671cfc902SMark Brown 	},
39771cfc902SMark Brown 	.probe =    wm8728_i2c_probe,
39871cfc902SMark Brown 	.remove =   wm8728_i2c_remove,
39971cfc902SMark Brown 	.id_table = wm8728_i2c_id,
40071cfc902SMark Brown };
40171cfc902SMark Brown 
40271cfc902SMark Brown static int wm8728_add_i2c_device(struct platform_device *pdev,
40371cfc902SMark Brown 				 const struct wm8728_setup_data *setup)
40471cfc902SMark Brown {
40571cfc902SMark Brown 	struct i2c_board_info info;
40671cfc902SMark Brown 	struct i2c_adapter *adapter;
40771cfc902SMark Brown 	struct i2c_client *client;
40871cfc902SMark Brown 	int ret;
40971cfc902SMark Brown 
41071cfc902SMark Brown 	ret = i2c_add_driver(&wm8728_i2c_driver);
41171cfc902SMark Brown 	if (ret != 0) {
41271cfc902SMark Brown 		dev_err(&pdev->dev, "can't add i2c driver\n");
41371cfc902SMark Brown 		return ret;
41471cfc902SMark Brown 	}
41571cfc902SMark Brown 
41671cfc902SMark Brown 	memset(&info, 0, sizeof(struct i2c_board_info));
41771cfc902SMark Brown 	info.addr = setup->i2c_address;
41871cfc902SMark Brown 	strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
41971cfc902SMark Brown 
42071cfc902SMark Brown 	adapter = i2c_get_adapter(setup->i2c_bus);
42171cfc902SMark Brown 	if (!adapter) {
42271cfc902SMark Brown 		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
42371cfc902SMark Brown 			setup->i2c_bus);
42471cfc902SMark Brown 		goto err_driver;
42571cfc902SMark Brown 	}
42671cfc902SMark Brown 
42771cfc902SMark Brown 	client = i2c_new_device(adapter, &info);
42871cfc902SMark Brown 	i2c_put_adapter(adapter);
42971cfc902SMark Brown 	if (!client) {
43071cfc902SMark Brown 		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
43171cfc902SMark Brown 			(unsigned int)info.addr);
43271cfc902SMark Brown 		goto err_driver;
43371cfc902SMark Brown 	}
43471cfc902SMark Brown 
43571cfc902SMark Brown 	return 0;
43671cfc902SMark Brown 
43771cfc902SMark Brown err_driver:
43871cfc902SMark Brown 	i2c_del_driver(&wm8728_i2c_driver);
43971cfc902SMark Brown 	return -ENODEV;
44071cfc902SMark Brown }
44171cfc902SMark Brown #endif
44271cfc902SMark Brown 
44371cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
44471cfc902SMark Brown static int __devinit wm8728_spi_probe(struct spi_device *spi)
44571cfc902SMark Brown {
44671cfc902SMark Brown 	struct snd_soc_device *socdev = wm8728_socdev;
44771cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
44871cfc902SMark Brown 	int ret;
44971cfc902SMark Brown 
45071cfc902SMark Brown 	codec->control_data = spi;
45171cfc902SMark Brown 
45271cfc902SMark Brown 	ret = wm8728_init(socdev);
45371cfc902SMark Brown 	if (ret < 0)
45471cfc902SMark Brown 		dev_err(&spi->dev, "failed to initialise WM8728\n");
45571cfc902SMark Brown 
45671cfc902SMark Brown 	return ret;
45771cfc902SMark Brown }
45871cfc902SMark Brown 
45971cfc902SMark Brown static int __devexit wm8728_spi_remove(struct spi_device *spi)
46071cfc902SMark Brown {
46171cfc902SMark Brown 	return 0;
46271cfc902SMark Brown }
46371cfc902SMark Brown 
46471cfc902SMark Brown static struct spi_driver wm8728_spi_driver = {
46571cfc902SMark Brown 	.driver = {
46671cfc902SMark Brown 		.name	= "wm8728",
46771cfc902SMark Brown 		.bus	= &spi_bus_type,
46871cfc902SMark Brown 		.owner	= THIS_MODULE,
46971cfc902SMark Brown 	},
47071cfc902SMark Brown 	.probe		= wm8728_spi_probe,
47171cfc902SMark Brown 	.remove		= __devexit_p(wm8728_spi_remove),
47271cfc902SMark Brown };
47371cfc902SMark Brown 
47471cfc902SMark Brown static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
47571cfc902SMark Brown {
47671cfc902SMark Brown 	struct spi_transfer t;
47771cfc902SMark Brown 	struct spi_message m;
47871cfc902SMark Brown 	u8 msg[2];
47971cfc902SMark Brown 
48071cfc902SMark Brown 	if (len <= 0)
48171cfc902SMark Brown 		return 0;
48271cfc902SMark Brown 
48371cfc902SMark Brown 	msg[0] = data[0];
48471cfc902SMark Brown 	msg[1] = data[1];
48571cfc902SMark Brown 
48671cfc902SMark Brown 	spi_message_init(&m);
48771cfc902SMark Brown 	memset(&t, 0, (sizeof t));
48871cfc902SMark Brown 
48971cfc902SMark Brown 	t.tx_buf = &msg[0];
49071cfc902SMark Brown 	t.len = len;
49171cfc902SMark Brown 
49271cfc902SMark Brown 	spi_message_add_tail(&t, &m);
49371cfc902SMark Brown 	spi_sync(spi, &m);
49471cfc902SMark Brown 
49571cfc902SMark Brown 	return len;
49671cfc902SMark Brown }
49771cfc902SMark Brown #endif /* CONFIG_SPI_MASTER */
49871cfc902SMark Brown 
49971cfc902SMark Brown static int wm8728_probe(struct platform_device *pdev)
50071cfc902SMark Brown {
50171cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
50271cfc902SMark Brown 	struct wm8728_setup_data *setup;
50371cfc902SMark Brown 	struct snd_soc_codec *codec;
50471cfc902SMark Brown 	int ret = 0;
50571cfc902SMark Brown 
50671cfc902SMark Brown 	setup = socdev->codec_data;
50771cfc902SMark Brown 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
50871cfc902SMark Brown 	if (codec == NULL)
50971cfc902SMark Brown 		return -ENOMEM;
51071cfc902SMark Brown 
51171cfc902SMark Brown 	socdev->codec = codec;
51271cfc902SMark Brown 	mutex_init(&codec->mutex);
51371cfc902SMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
51471cfc902SMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
51571cfc902SMark Brown 
51671cfc902SMark Brown 	wm8728_socdev = socdev;
51771cfc902SMark Brown 	ret = -ENODEV;
51871cfc902SMark Brown 
51971cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
52071cfc902SMark Brown 	if (setup->i2c_address) {
52171cfc902SMark Brown 		codec->hw_write = (hw_write_t)i2c_master_send;
52271cfc902SMark Brown 		ret = wm8728_add_i2c_device(pdev, setup);
52371cfc902SMark Brown 	}
52471cfc902SMark Brown #endif
52571cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
52671cfc902SMark Brown 	if (setup->spi) {
52771cfc902SMark Brown 		codec->hw_write = (hw_write_t)wm8728_spi_write;
52871cfc902SMark Brown 		ret = spi_register_driver(&wm8728_spi_driver);
52971cfc902SMark Brown 		if (ret != 0)
53071cfc902SMark Brown 			printk(KERN_ERR "can't add spi driver");
53171cfc902SMark Brown 	}
53271cfc902SMark Brown #endif
53371cfc902SMark Brown 
53471cfc902SMark Brown 	if (ret != 0)
53571cfc902SMark Brown 		kfree(codec);
53671cfc902SMark Brown 
53771cfc902SMark Brown 	return ret;
53871cfc902SMark Brown }
53971cfc902SMark Brown 
54071cfc902SMark Brown /* power down chip */
54171cfc902SMark Brown static int wm8728_remove(struct platform_device *pdev)
54271cfc902SMark Brown {
54371cfc902SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
54471cfc902SMark Brown 	struct snd_soc_codec *codec = socdev->codec;
54571cfc902SMark Brown 
54671cfc902SMark Brown 	if (codec->control_data)
54771cfc902SMark Brown 		wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
54871cfc902SMark Brown 
54971cfc902SMark Brown 	snd_soc_free_pcms(socdev);
55071cfc902SMark Brown 	snd_soc_dapm_free(socdev);
55171cfc902SMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
55271cfc902SMark Brown 	i2c_unregister_device(codec->control_data);
55371cfc902SMark Brown 	i2c_del_driver(&wm8728_i2c_driver);
55471cfc902SMark Brown #endif
55571cfc902SMark Brown #if defined(CONFIG_SPI_MASTER)
55671cfc902SMark Brown 	spi_unregister_driver(&wm8728_spi_driver);
55771cfc902SMark Brown #endif
55871cfc902SMark Brown 	kfree(codec);
55971cfc902SMark Brown 
56071cfc902SMark Brown 	return 0;
56171cfc902SMark Brown }
56271cfc902SMark Brown 
56371cfc902SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8728 = {
56471cfc902SMark Brown 	.probe = 	wm8728_probe,
56571cfc902SMark Brown 	.remove = 	wm8728_remove,
56671cfc902SMark Brown 	.suspend = 	wm8728_suspend,
56771cfc902SMark Brown 	.resume =	wm8728_resume,
56871cfc902SMark Brown };
56971cfc902SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
57071cfc902SMark Brown 
571c9b3a40fSTakashi Iwai static int __init wm8728_modinit(void)
57264089b84SMark Brown {
57364089b84SMark Brown 	return snd_soc_register_dai(&wm8728_dai);
57464089b84SMark Brown }
57564089b84SMark Brown module_init(wm8728_modinit);
57664089b84SMark Brown 
57764089b84SMark Brown static void __exit wm8728_exit(void)
57864089b84SMark Brown {
57964089b84SMark Brown 	snd_soc_unregister_dai(&wm8728_dai);
58064089b84SMark Brown }
58164089b84SMark Brown module_exit(wm8728_exit);
58264089b84SMark Brown 
58371cfc902SMark Brown MODULE_DESCRIPTION("ASoC WM8728 driver");
58471cfc902SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
58571cfc902SMark Brown MODULE_LICENSE("GPL");
586