| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 1 | /* | 
 | 2 |  * ALSA SoC WL1273 codec driver | 
 | 3 |  * | 
 | 4 |  * Author:      Matti Aaltonen, <matti.j.aaltonen@nokia.com> | 
 | 5 |  * | 
| Matti Aaltonen | 40285f8 | 2011-03-01 10:10:37 -0300 | [diff] [blame] | 6 |  * Copyright:   (C) 2010, 2011 Nokia Corporation | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 7 |  * | 
 | 8 |  * This program is free software; you can redistribute it and/or | 
 | 9 |  * modify it under the terms of the GNU General Public License | 
 | 10 |  * version 2 as published by the Free Software Foundation. | 
 | 11 |  * | 
 | 12 |  * This program is distributed in the hope that it will be useful, but | 
 | 13 |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 14 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 15 |  * General Public License for more details. | 
 | 16 |  * | 
 | 17 |  * You should have received a copy of the GNU General Public License | 
 | 18 |  * along with this program; if not, write to the Free Software | 
 | 19 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 
 | 20 |  * 02110-1301 USA | 
 | 21 |  * | 
 | 22 |  */ | 
 | 23 |  | 
 | 24 | #include <linux/mfd/wl1273-core.h> | 
 | 25 | #include <linux/slab.h> | 
 | 26 | #include <sound/pcm.h> | 
 | 27 | #include <sound/pcm_params.h> | 
| Jarkko Nikula | 0d911bae | 2010-11-21 19:48:46 +0200 | [diff] [blame] | 28 | #include <sound/soc.h> | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 29 | #include <sound/initval.h> | 
 | 30 |  | 
 | 31 | #include "wl1273.h" | 
 | 32 |  | 
 | 33 | enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX }; | 
 | 34 |  | 
 | 35 | /* codec private data */ | 
 | 36 | struct wl1273_priv { | 
 | 37 | 	enum wl1273_mode mode; | 
 | 38 | 	struct wl1273_core *core; | 
 | 39 | 	unsigned int channels; | 
 | 40 | }; | 
 | 41 |  | 
 | 42 | static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, | 
 | 43 | 				      int rate, int width) | 
 | 44 | { | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 45 | 	struct device *dev = &core->client->dev; | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 46 | 	int r = 0; | 
 | 47 | 	u16 mode; | 
 | 48 |  | 
 | 49 | 	dev_dbg(dev, "rate: %d\n", rate); | 
 | 50 | 	dev_dbg(dev, "width: %d\n", width); | 
 | 51 |  | 
 | 52 | 	mutex_lock(&core->lock); | 
 | 53 |  | 
 | 54 | 	mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE; | 
 | 55 |  | 
 | 56 | 	switch (rate) { | 
 | 57 | 	case 48000: | 
 | 58 | 		mode |= WL1273_IS2_RATE_48K; | 
 | 59 | 		break; | 
 | 60 | 	case 44100: | 
 | 61 | 		mode |= WL1273_IS2_RATE_44_1K; | 
 | 62 | 		break; | 
 | 63 | 	case 32000: | 
 | 64 | 		mode |= WL1273_IS2_RATE_32K; | 
 | 65 | 		break; | 
 | 66 | 	case 22050: | 
 | 67 | 		mode |= WL1273_IS2_RATE_22_05K; | 
 | 68 | 		break; | 
 | 69 | 	case 16000: | 
 | 70 | 		mode |= WL1273_IS2_RATE_16K; | 
 | 71 | 		break; | 
 | 72 | 	case 12000: | 
 | 73 | 		mode |= WL1273_IS2_RATE_12K; | 
 | 74 | 		break; | 
 | 75 | 	case 11025: | 
 | 76 | 		mode |= WL1273_IS2_RATE_11_025; | 
 | 77 | 		break; | 
 | 78 | 	case 8000: | 
 | 79 | 		mode |= WL1273_IS2_RATE_8K; | 
 | 80 | 		break; | 
 | 81 | 	default: | 
 | 82 | 		dev_err(dev, "Sampling rate: %d not supported\n", rate); | 
 | 83 | 		r = -EINVAL; | 
 | 84 | 		goto out; | 
 | 85 | 	} | 
 | 86 |  | 
 | 87 | 	switch (width) { | 
 | 88 | 	case 16: | 
 | 89 | 		mode |= WL1273_IS2_WIDTH_32; | 
 | 90 | 		break; | 
 | 91 | 	case 20: | 
 | 92 | 		mode |= WL1273_IS2_WIDTH_40; | 
 | 93 | 		break; | 
 | 94 | 	case 24: | 
 | 95 | 		mode |= WL1273_IS2_WIDTH_48; | 
 | 96 | 		break; | 
 | 97 | 	case 25: | 
 | 98 | 		mode |= WL1273_IS2_WIDTH_50; | 
 | 99 | 		break; | 
 | 100 | 	case 30: | 
 | 101 | 		mode |= WL1273_IS2_WIDTH_60; | 
 | 102 | 		break; | 
 | 103 | 	case 32: | 
 | 104 | 		mode |= WL1273_IS2_WIDTH_64; | 
 | 105 | 		break; | 
 | 106 | 	case 40: | 
 | 107 | 		mode |= WL1273_IS2_WIDTH_80; | 
 | 108 | 		break; | 
 | 109 | 	case 48: | 
 | 110 | 		mode |= WL1273_IS2_WIDTH_96; | 
 | 111 | 		break; | 
 | 112 | 	case 64: | 
 | 113 | 		mode |= WL1273_IS2_WIDTH_128; | 
 | 114 | 		break; | 
 | 115 | 	default: | 
 | 116 | 		dev_err(dev, "Data width: %d not supported\n", width); | 
 | 117 | 		r = -EINVAL; | 
 | 118 | 		goto out; | 
 | 119 | 	} | 
 | 120 |  | 
 | 121 | 	dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n",  WL1273_I2S_DEF_MODE); | 
 | 122 | 	dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode); | 
 | 123 | 	dev_dbg(dev, "mode: 0x%04x\n", mode); | 
 | 124 |  | 
 | 125 | 	if (core->i2s_mode != mode) { | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 126 | 		r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 127 | 		if (r) | 
 | 128 | 			goto out; | 
 | 129 |  | 
 | 130 | 		core->i2s_mode = mode; | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 131 | 		r = core->write(core, WL1273_AUDIO_ENABLE, | 
 | 132 | 				WL1273_AUDIO_ENABLE_I2S); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 133 | 		if (r) | 
 | 134 | 			goto out; | 
 | 135 | 	} | 
 | 136 | out: | 
 | 137 | 	mutex_unlock(&core->lock); | 
 | 138 |  | 
 | 139 | 	return r; | 
 | 140 | } | 
 | 141 |  | 
 | 142 | static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, | 
 | 143 | 					    int channel_number) | 
 | 144 | { | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 145 | 	struct device *dev = &core->client->dev; | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 146 | 	int r = 0; | 
 | 147 |  | 
 | 148 | 	dev_dbg(dev, "%s\n", __func__); | 
 | 149 |  | 
 | 150 | 	mutex_lock(&core->lock); | 
 | 151 |  | 
 | 152 | 	if (core->channel_number == channel_number) | 
 | 153 | 		goto out; | 
 | 154 |  | 
 | 155 | 	if (channel_number == 1 && core->mode == WL1273_MODE_RX) | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 156 | 		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 157 | 	else if (channel_number == 1 && core->mode == WL1273_MODE_TX) | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 158 | 		r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 159 | 	else if (channel_number == 2 && core->mode == WL1273_MODE_RX) | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 160 | 		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 161 | 	else if (channel_number == 2 && core->mode == WL1273_MODE_TX) | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 162 | 		r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 163 | 	else | 
 | 164 | 		r = -EINVAL; | 
 | 165 | out: | 
 | 166 | 	mutex_unlock(&core->lock); | 
 | 167 |  | 
 | 168 | 	return r; | 
 | 169 | } | 
 | 170 |  | 
 | 171 | static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, | 
 | 172 | 				      struct snd_ctl_elem_value *ucontrol) | 
 | 173 | { | 
 | 174 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 175 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 176 |  | 
 | 177 | 	ucontrol->value.integer.value[0] = wl1273->mode; | 
 | 178 |  | 
 | 179 | 	return 0; | 
 | 180 | } | 
 | 181 |  | 
| Matti Aaltonen | 40285f8 | 2011-03-01 10:10:37 -0300 | [diff] [blame] | 182 | /* | 
 | 183 |  * TODO: Implement the audio routing in the driver. Now this control | 
 | 184 |  * only indicates the setting that has been done elsewhere (in the user | 
 | 185 |  * space). | 
 | 186 |  */ | 
 | 187 | static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 188 |  | 
 | 189 | static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, | 
 | 190 | 				      struct snd_ctl_elem_value *ucontrol) | 
 | 191 | { | 
 | 192 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 193 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 194 |  | 
| Matti J. Aaltonen | 2c4ee9b | 2010-09-10 10:41:30 +0300 | [diff] [blame] | 195 | 	if (wl1273->mode == ucontrol->value.integer.value[0]) | 
 | 196 | 		return 0; | 
 | 197 |  | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 198 | 	/* Do not allow changes while stream is running */ | 
 | 199 | 	if (codec->active) | 
 | 200 | 		return -EPERM; | 
 | 201 |  | 
 | 202 | 	if (ucontrol->value.integer.value[0] < 0 || | 
 | 203 | 	    ucontrol->value.integer.value[0] >=  ARRAY_SIZE(wl1273_audio_route)) | 
 | 204 | 		return -EINVAL; | 
 | 205 |  | 
 | 206 | 	wl1273->mode = ucontrol->value.integer.value[0]; | 
 | 207 |  | 
 | 208 | 	return 1; | 
 | 209 | } | 
 | 210 |  | 
 | 211 | static const struct soc_enum wl1273_enum = | 
 | 212 | 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_route), wl1273_audio_route); | 
 | 213 |  | 
 | 214 | static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, | 
 | 215 | 				   struct snd_ctl_elem_value *ucontrol) | 
 | 216 | { | 
 | 217 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 218 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 219 |  | 
 | 220 | 	dev_dbg(codec->dev, "%s: enter.\n", __func__); | 
 | 221 |  | 
 | 222 | 	ucontrol->value.integer.value[0] = wl1273->core->audio_mode; | 
 | 223 |  | 
 | 224 | 	return 0; | 
 | 225 | } | 
 | 226 |  | 
 | 227 | static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, | 
 | 228 | 				   struct snd_ctl_elem_value *ucontrol) | 
 | 229 | { | 
 | 230 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 231 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 232 | 	int val, r = 0; | 
 | 233 |  | 
 | 234 | 	dev_dbg(codec->dev, "%s: enter.\n", __func__); | 
 | 235 |  | 
 | 236 | 	val = ucontrol->value.integer.value[0]; | 
 | 237 | 	if (wl1273->core->audio_mode == val) | 
 | 238 | 		return 0; | 
 | 239 |  | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 240 | 	r = wl1273->core->set_audio(wl1273->core, val); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 241 | 	if (r < 0) | 
 | 242 | 		return r; | 
 | 243 |  | 
 | 244 | 	return 1; | 
 | 245 | } | 
 | 246 |  | 
| Matti Aaltonen | 40285f8 | 2011-03-01 10:10:37 -0300 | [diff] [blame] | 247 | static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 248 |  | 
 | 249 | static const struct soc_enum wl1273_audio_enum = | 
 | 250 | 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), | 
 | 251 | 			    wl1273_audio_strings); | 
 | 252 |  | 
 | 253 | static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, | 
 | 254 | 				    struct snd_ctl_elem_value *ucontrol) | 
 | 255 | { | 
 | 256 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 257 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 258 |  | 
 | 259 | 	dev_dbg(codec->dev, "%s: enter.\n", __func__); | 
 | 260 |  | 
 | 261 | 	ucontrol->value.integer.value[0] = wl1273->core->volume; | 
 | 262 |  | 
 | 263 | 	return 0; | 
 | 264 | } | 
 | 265 |  | 
 | 266 | static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, | 
 | 267 | 				    struct snd_ctl_elem_value *ucontrol) | 
 | 268 | { | 
 | 269 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 270 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 271 | 	int r; | 
 | 272 |  | 
 | 273 | 	dev_dbg(codec->dev, "%s: enter.\n", __func__); | 
 | 274 |  | 
| Matti J. Aaltonen | 228dd54 | 2011-01-13 15:22:45 +0200 | [diff] [blame] | 275 | 	r = wl1273->core->set_volume(wl1273->core, | 
 | 276 | 				     ucontrol->value.integer.value[0]); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 277 | 	if (r) | 
 | 278 | 		return r; | 
 | 279 |  | 
 | 280 | 	return 1; | 
 | 281 | } | 
 | 282 |  | 
 | 283 | static const struct snd_kcontrol_new wl1273_controls[] = { | 
 | 284 | 	SOC_ENUM_EXT("Codec Mode", wl1273_enum, | 
 | 285 | 		     snd_wl1273_get_audio_route, snd_wl1273_set_audio_route), | 
 | 286 | 	SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum, | 
 | 287 | 		     snd_wl1273_fm_audio_get,  snd_wl1273_fm_audio_put), | 
 | 288 | 	SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0, | 
 | 289 | 		       snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), | 
 | 290 | }; | 
 | 291 |  | 
 | 292 | static int wl1273_startup(struct snd_pcm_substream *substream, | 
 | 293 | 			  struct snd_soc_dai *dai) | 
 | 294 | { | 
 | 295 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
 | 296 | 	struct snd_soc_codec *codec = rtd->codec; | 
 | 297 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 298 |  | 
 | 299 | 	switch (wl1273->mode) { | 
 | 300 | 	case WL1273_MODE_BT: | 
 | 301 | 		snd_pcm_hw_constraint_minmax(substream->runtime, | 
 | 302 | 					     SNDRV_PCM_HW_PARAM_RATE, | 
 | 303 | 					     8000, 8000); | 
 | 304 | 		snd_pcm_hw_constraint_minmax(substream->runtime, | 
 | 305 | 					     SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1); | 
 | 306 | 		break; | 
 | 307 | 	case WL1273_MODE_FM_RX: | 
 | 308 | 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
 | 309 | 			pr_err("Cannot play in RX mode.\n"); | 
 | 310 | 			return -EINVAL; | 
 | 311 | 		} | 
 | 312 | 		break; | 
 | 313 | 	case WL1273_MODE_FM_TX: | 
 | 314 | 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | 
 | 315 | 			pr_err("Cannot capture in TX mode.\n"); | 
 | 316 | 			return -EINVAL; | 
 | 317 | 		} | 
 | 318 | 		break; | 
 | 319 | 	default: | 
 | 320 | 		return -EINVAL; | 
 | 321 | 		break; | 
 | 322 | 	} | 
 | 323 |  | 
 | 324 | 	return 0; | 
 | 325 | } | 
 | 326 |  | 
 | 327 | static int wl1273_hw_params(struct snd_pcm_substream *substream, | 
 | 328 | 			    struct snd_pcm_hw_params *params, | 
 | 329 | 			    struct snd_soc_dai *dai) | 
 | 330 | { | 
 | 331 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
 | 332 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(rtd->codec); | 
 | 333 | 	struct wl1273_core *core = wl1273->core; | 
 | 334 | 	unsigned int rate, width, r; | 
 | 335 |  | 
 | 336 | 	if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { | 
 | 337 | 		pr_err("Only SNDRV_PCM_FORMAT_S16_LE supported.\n"); | 
 | 338 | 		return -EINVAL; | 
 | 339 | 	} | 
 | 340 |  | 
 | 341 | 	rate = params_rate(params); | 
 | 342 | 	width =  hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; | 
 | 343 |  | 
 | 344 | 	if (wl1273->mode == WL1273_MODE_BT) { | 
 | 345 | 		if (rate != 8000) { | 
 | 346 | 			pr_err("Rate %d not supported.\n", params_rate(params)); | 
 | 347 | 			return -EINVAL; | 
 | 348 | 		} | 
 | 349 |  | 
 | 350 | 		if (params_channels(params) != 1) { | 
 | 351 | 			pr_err("Only mono supported.\n"); | 
 | 352 | 			return -EINVAL; | 
 | 353 | 		} | 
 | 354 |  | 
 | 355 | 		return 0; | 
 | 356 | 	} | 
 | 357 |  | 
 | 358 | 	if (wl1273->mode == WL1273_MODE_FM_TX && | 
 | 359 | 	    substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | 
 | 360 | 		pr_err("Only playback supported with TX.\n"); | 
 | 361 | 		return -EINVAL; | 
 | 362 | 	} | 
 | 363 |  | 
 | 364 | 	if (wl1273->mode == WL1273_MODE_FM_RX  && | 
 | 365 | 	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
 | 366 | 		pr_err("Only capture supported with RX.\n"); | 
 | 367 | 		return -EINVAL; | 
 | 368 | 	} | 
 | 369 |  | 
 | 370 | 	if (wl1273->mode != WL1273_MODE_FM_RX  && | 
 | 371 | 	    wl1273->mode != WL1273_MODE_FM_TX) { | 
 | 372 | 		pr_err("Unexpected mode: %d.\n", wl1273->mode); | 
 | 373 | 		return -EINVAL; | 
 | 374 | 	} | 
 | 375 |  | 
 | 376 | 	r = snd_wl1273_fm_set_i2s_mode(core, rate, width); | 
 | 377 | 	if (r) | 
 | 378 | 		return r; | 
 | 379 |  | 
 | 380 | 	wl1273->channels = params_channels(params); | 
 | 381 | 	r = snd_wl1273_fm_set_channel_number(core, wl1273->channels); | 
 | 382 | 	if (r) | 
 | 383 | 		return r; | 
 | 384 |  | 
 | 385 | 	return 0; | 
 | 386 | } | 
 | 387 |  | 
 | 388 | static struct snd_soc_dai_ops wl1273_dai_ops = { | 
 | 389 | 	.startup	= wl1273_startup, | 
 | 390 | 	.hw_params	= wl1273_hw_params, | 
 | 391 | }; | 
 | 392 |  | 
 | 393 | static struct snd_soc_dai_driver wl1273_dai = { | 
 | 394 | 	.name = "wl1273-fm", | 
 | 395 | 	.playback = { | 
 | 396 | 		.stream_name = "Playback", | 
 | 397 | 		.channels_min = 1, | 
 | 398 | 		.channels_max = 2, | 
 | 399 | 		.rates = SNDRV_PCM_RATE_8000_48000, | 
 | 400 | 		.formats = SNDRV_PCM_FMTBIT_S16_LE}, | 
 | 401 | 	.capture = { | 
 | 402 | 		.stream_name = "Capture", | 
 | 403 | 		.channels_min = 1, | 
 | 404 | 		.channels_max = 2, | 
 | 405 | 		.rates = SNDRV_PCM_RATE_8000_48000, | 
 | 406 | 		.formats = SNDRV_PCM_FMTBIT_S16_LE}, | 
 | 407 | 	.ops = &wl1273_dai_ops, | 
 | 408 | }; | 
 | 409 |  | 
 | 410 | /* Audio interface format for the soc_card driver */ | 
 | 411 | int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt) | 
 | 412 | { | 
 | 413 | 	struct wl1273_priv *wl1273; | 
 | 414 |  | 
 | 415 | 	if (codec == NULL || fmt == NULL) | 
 | 416 | 		return -EINVAL; | 
 | 417 |  | 
 | 418 | 	wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 419 |  | 
 | 420 | 	switch (wl1273->mode) { | 
 | 421 | 	case WL1273_MODE_FM_RX: | 
 | 422 | 	case WL1273_MODE_FM_TX: | 
 | 423 | 		*fmt =	SND_SOC_DAIFMT_I2S | | 
 | 424 | 			SND_SOC_DAIFMT_NB_NF | | 
 | 425 | 			SND_SOC_DAIFMT_CBM_CFM; | 
 | 426 |  | 
 | 427 | 		break; | 
 | 428 | 	case WL1273_MODE_BT: | 
 | 429 | 		*fmt =	SND_SOC_DAIFMT_DSP_A | | 
 | 430 | 			SND_SOC_DAIFMT_IB_NF | | 
 | 431 | 			SND_SOC_DAIFMT_CBM_CFM; | 
 | 432 |  | 
 | 433 | 		break; | 
 | 434 | 	default: | 
 | 435 | 		return -EINVAL; | 
 | 436 | 	} | 
 | 437 |  | 
 | 438 | 	return 0; | 
 | 439 | } | 
 | 440 | EXPORT_SYMBOL_GPL(wl1273_get_format); | 
 | 441 |  | 
 | 442 | static int wl1273_probe(struct snd_soc_codec *codec) | 
 | 443 | { | 
| Andres Salomon | 15de7a4 | 2011-02-17 19:07:17 -0800 | [diff] [blame] | 444 | 	struct wl1273_core **core = | 
 | 445 | 			mfd_get_data(to_platform_device(codec->dev)); | 
| Matti J. Aaltonen | 3fabe08 | 2010-08-20 12:32:46 +0300 | [diff] [blame] | 446 | 	struct wl1273_priv *wl1273; | 
 | 447 | 	int r; | 
 | 448 |  | 
 | 449 | 	dev_dbg(codec->dev, "%s.\n", __func__); | 
 | 450 |  | 
 | 451 | 	if (!core) { | 
 | 452 | 		dev_err(codec->dev, "Platform data is missing.\n"); | 
 | 453 | 		return -EINVAL; | 
 | 454 | 	} | 
 | 455 |  | 
 | 456 | 	wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL); | 
 | 457 | 	if (wl1273 == NULL) { | 
 | 458 | 		dev_err(codec->dev, "Cannot allocate memory.\n"); | 
 | 459 | 		return -ENOMEM; | 
 | 460 | 	} | 
 | 461 |  | 
 | 462 | 	wl1273->mode = WL1273_MODE_BT; | 
 | 463 | 	wl1273->core = *core; | 
 | 464 |  | 
 | 465 | 	snd_soc_codec_set_drvdata(codec, wl1273); | 
 | 466 | 	mutex_init(&codec->mutex); | 
 | 467 |  | 
 | 468 | 	r = snd_soc_add_controls(codec, wl1273_controls, | 
 | 469 | 				 ARRAY_SIZE(wl1273_controls)); | 
 | 470 | 	if (r) | 
 | 471 | 		kfree(wl1273); | 
 | 472 |  | 
 | 473 | 	return r; | 
 | 474 | } | 
 | 475 |  | 
 | 476 | static int wl1273_remove(struct snd_soc_codec *codec) | 
 | 477 | { | 
 | 478 | 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | 
 | 479 |  | 
 | 480 | 	dev_dbg(codec->dev, "%s\n", __func__); | 
 | 481 | 	kfree(wl1273); | 
 | 482 |  | 
 | 483 | 	return 0; | 
 | 484 | } | 
 | 485 |  | 
 | 486 | static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { | 
 | 487 | 	.probe = wl1273_probe, | 
 | 488 | 	.remove = wl1273_remove, | 
 | 489 | }; | 
 | 490 |  | 
 | 491 | static int __devinit wl1273_platform_probe(struct platform_device *pdev) | 
 | 492 | { | 
 | 493 | 	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wl1273, | 
 | 494 | 				      &wl1273_dai, 1); | 
 | 495 | } | 
 | 496 |  | 
 | 497 | static int __devexit wl1273_platform_remove(struct platform_device *pdev) | 
 | 498 | { | 
 | 499 | 	snd_soc_unregister_codec(&pdev->dev); | 
 | 500 | 	return 0; | 
 | 501 | } | 
 | 502 |  | 
 | 503 | MODULE_ALIAS("platform:wl1273-codec"); | 
 | 504 |  | 
 | 505 | static struct platform_driver wl1273_platform_driver = { | 
 | 506 | 	.driver		= { | 
 | 507 | 		.name	= "wl1273-codec", | 
 | 508 | 		.owner	= THIS_MODULE, | 
 | 509 | 	}, | 
 | 510 | 	.probe		= wl1273_platform_probe, | 
 | 511 | 	.remove		= __devexit_p(wl1273_platform_remove), | 
 | 512 | }; | 
 | 513 |  | 
 | 514 | static int __init wl1273_init(void) | 
 | 515 | { | 
 | 516 | 	return platform_driver_register(&wl1273_platform_driver); | 
 | 517 | } | 
 | 518 | module_init(wl1273_init); | 
 | 519 |  | 
 | 520 | static void __exit wl1273_exit(void) | 
 | 521 | { | 
 | 522 | 	platform_driver_unregister(&wl1273_platform_driver); | 
 | 523 | } | 
 | 524 | module_exit(wl1273_exit); | 
 | 525 |  | 
 | 526 | MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); | 
 | 527 | MODULE_DESCRIPTION("ASoC WL1273 codec driver"); | 
 | 528 | MODULE_LICENSE("GPL"); |