diff options
Diffstat (limited to 'sound/soc/codecs/wm8728.c')
-rw-r--r-- | sound/soc/codecs/wm8728.c | 294 |
1 files changed, 95 insertions, 199 deletions
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 34be2d2b69ef..ae2292444783 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c | |||
@@ -29,8 +29,6 @@ | |||
29 | 29 | ||
30 | #include "wm8728.h" | 30 | #include "wm8728.h" |
31 | 31 | ||
32 | struct snd_soc_codec_device soc_codec_dev_wm8728; | ||
33 | |||
34 | /* | 32 | /* |
35 | * We can't read the WM8728 register space so we cache them instead. | 33 | * We can't read the WM8728 register space so we cache them instead. |
36 | * Note that the defaults here aren't the physical defaults, we latch | 34 | * Note that the defaults here aren't the physical defaults, we latch |
@@ -44,6 +42,12 @@ static const u16 wm8728_reg_defaults[] = { | |||
44 | 0x100, | 42 | 0x100, |
45 | }; | 43 | }; |
46 | 44 | ||
45 | /* codec private data */ | ||
46 | struct wm8728_priv { | ||
47 | enum snd_soc_control_type control_type; | ||
48 | void *control_data; | ||
49 | }; | ||
50 | |||
47 | static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1); | 51 | static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1); |
48 | 52 | ||
49 | static const struct snd_kcontrol_new wm8728_snd_controls[] = { | 53 | static const struct snd_kcontrol_new wm8728_snd_controls[] = { |
@@ -96,8 +100,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream, | |||
96 | struct snd_soc_dai *dai) | 100 | struct snd_soc_dai *dai) |
97 | { | 101 | { |
98 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 102 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
99 | struct snd_soc_device *socdev = rtd->socdev; | 103 | struct snd_soc_codec *codec = rtd->codec; |
100 | struct snd_soc_codec *codec = socdev->card->codec; | ||
101 | u16 dac = snd_soc_read(codec, WM8728_DACCTL); | 104 | u16 dac = snd_soc_read(codec, WM8728_DACCTL); |
102 | 105 | ||
103 | dac &= ~0x18; | 106 | dac &= ~0x18; |
@@ -210,8 +213,8 @@ static struct snd_soc_dai_ops wm8728_dai_ops = { | |||
210 | .set_fmt = wm8728_set_dai_fmt, | 213 | .set_fmt = wm8728_set_dai_fmt, |
211 | }; | 214 | }; |
212 | 215 | ||
213 | struct snd_soc_dai wm8728_dai = { | 216 | static struct snd_soc_dai_driver wm8728_dai = { |
214 | .name = "WM8728", | 217 | .name = "wm8728-hifi", |
215 | .playback = { | 218 | .playback = { |
216 | .stream_name = "Playback", | 219 | .stream_name = "Playback", |
217 | .channels_min = 2, | 220 | .channels_min = 2, |
@@ -221,63 +224,32 @@ struct snd_soc_dai wm8728_dai = { | |||
221 | }, | 224 | }, |
222 | .ops = &wm8728_dai_ops, | 225 | .ops = &wm8728_dai_ops, |
223 | }; | 226 | }; |
224 | EXPORT_SYMBOL_GPL(wm8728_dai); | ||
225 | 227 | ||
226 | static int wm8728_suspend(struct platform_device *pdev, pm_message_t state) | 228 | static int wm8728_suspend(struct snd_soc_codec *codec, pm_message_t state) |
227 | { | 229 | { |
228 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
229 | struct snd_soc_codec *codec = socdev->card->codec; | ||
230 | |||
231 | wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); | 230 | wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); |
232 | 231 | ||
233 | return 0; | 232 | return 0; |
234 | } | 233 | } |
235 | 234 | ||
236 | static int wm8728_resume(struct platform_device *pdev) | 235 | static int wm8728_resume(struct snd_soc_codec *codec) |
237 | { | 236 | { |
238 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
239 | struct snd_soc_codec *codec = socdev->card->codec; | ||
240 | |||
241 | wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 237 | wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
242 | 238 | ||
243 | return 0; | 239 | return 0; |
244 | } | 240 | } |
245 | 241 | ||
246 | /* | 242 | static int wm8728_probe(struct snd_soc_codec *codec) |
247 | * initialise the WM8728 driver | ||
248 | * register the mixer and dsp interfaces with the kernel | ||
249 | */ | ||
250 | static int wm8728_init(struct snd_soc_device *socdev, | ||
251 | enum snd_soc_control_type control) | ||
252 | { | 243 | { |
253 | struct snd_soc_codec *codec = socdev->card->codec; | 244 | struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec); |
254 | int ret = 0; | 245 | int ret; |
255 | |||
256 | codec->name = "WM8728"; | ||
257 | codec->owner = THIS_MODULE; | ||
258 | codec->set_bias_level = wm8728_set_bias_level; | ||
259 | codec->dai = &wm8728_dai; | ||
260 | codec->num_dai = 1; | ||
261 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
262 | codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults); | ||
263 | codec->reg_cache = kmemdup(wm8728_reg_defaults, | ||
264 | sizeof(wm8728_reg_defaults), | ||
265 | GFP_KERNEL); | ||
266 | if (codec->reg_cache == NULL) | ||
267 | return -ENOMEM; | ||
268 | 246 | ||
269 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); | 247 | codec->control_data = wm8728->control_data; |
248 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8728->control_type); | ||
270 | if (ret < 0) { | 249 | if (ret < 0) { |
271 | printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n", | 250 | printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n", |
272 | ret); | 251 | ret); |
273 | goto err; | 252 | return ret; |
274 | } | ||
275 | |||
276 | /* register pcms */ | ||
277 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
278 | if (ret < 0) { | ||
279 | printk(KERN_ERR "wm8728: failed to create pcms\n"); | ||
280 | goto err; | ||
281 | } | 253 | } |
282 | 254 | ||
283 | /* power on device */ | 255 | /* power on device */ |
@@ -288,128 +260,56 @@ static int wm8728_init(struct snd_soc_device *socdev, | |||
288 | wm8728_add_widgets(codec); | 260 | wm8728_add_widgets(codec); |
289 | 261 | ||
290 | return ret; | 262 | return ret; |
291 | |||
292 | err: | ||
293 | kfree(codec->reg_cache); | ||
294 | return ret; | ||
295 | } | 263 | } |
296 | 264 | ||
297 | static struct snd_soc_device *wm8728_socdev; | 265 | static int wm8728_remove(struct snd_soc_codec *codec) |
298 | |||
299 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
300 | |||
301 | /* | ||
302 | * WM8728 2 wire address is determined by GPIO5 | ||
303 | * state during powerup. | ||
304 | * low = 0x1a | ||
305 | * high = 0x1b | ||
306 | */ | ||
307 | |||
308 | static int wm8728_i2c_probe(struct i2c_client *i2c, | ||
309 | const struct i2c_device_id *id) | ||
310 | { | 266 | { |
311 | struct snd_soc_device *socdev = wm8728_socdev; | 267 | wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); |
312 | struct snd_soc_codec *codec = socdev->card->codec; | ||
313 | int ret; | ||
314 | |||
315 | i2c_set_clientdata(i2c, codec); | ||
316 | codec->control_data = i2c; | ||
317 | |||
318 | ret = wm8728_init(socdev, SND_SOC_I2C); | ||
319 | if (ret < 0) | ||
320 | pr_err("failed to initialise WM8728\n"); | ||
321 | |||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | static int wm8728_i2c_remove(struct i2c_client *client) | ||
326 | { | ||
327 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
328 | kfree(codec->reg_cache); | ||
329 | return 0; | 268 | return 0; |
330 | } | 269 | } |
331 | 270 | ||
332 | static const struct i2c_device_id wm8728_i2c_id[] = { | 271 | static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { |
333 | { "wm8728", 0 }, | 272 | .probe = wm8728_probe, |
334 | { } | 273 | .remove = wm8728_remove, |
335 | }; | 274 | .suspend = wm8728_suspend, |
336 | MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); | 275 | .resume = wm8728_resume, |
337 | 276 | .set_bias_level = wm8728_set_bias_level, | |
338 | static struct i2c_driver wm8728_i2c_driver = { | 277 | .reg_cache_size = sizeof(wm8728_reg_defaults), |
339 | .driver = { | 278 | .reg_word_size = sizeof(u16), |
340 | .name = "WM8728 I2C Codec", | 279 | .reg_cache_default = wm8728_reg_defaults, |
341 | .owner = THIS_MODULE, | ||
342 | }, | ||
343 | .probe = wm8728_i2c_probe, | ||
344 | .remove = wm8728_i2c_remove, | ||
345 | .id_table = wm8728_i2c_id, | ||
346 | }; | 280 | }; |
347 | 281 | ||
348 | static int wm8728_add_i2c_device(struct platform_device *pdev, | ||
349 | const struct wm8728_setup_data *setup) | ||
350 | { | ||
351 | struct i2c_board_info info; | ||
352 | struct i2c_adapter *adapter; | ||
353 | struct i2c_client *client; | ||
354 | int ret; | ||
355 | |||
356 | ret = i2c_add_driver(&wm8728_i2c_driver); | ||
357 | if (ret != 0) { | ||
358 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
363 | info.addr = setup->i2c_address; | ||
364 | strlcpy(info.type, "wm8728", I2C_NAME_SIZE); | ||
365 | |||
366 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
367 | if (!adapter) { | ||
368 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
369 | setup->i2c_bus); | ||
370 | goto err_driver; | ||
371 | } | ||
372 | |||
373 | client = i2c_new_device(adapter, &info); | ||
374 | i2c_put_adapter(adapter); | ||
375 | if (!client) { | ||
376 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
377 | (unsigned int)info.addr); | ||
378 | goto err_driver; | ||
379 | } | ||
380 | |||
381 | return 0; | ||
382 | |||
383 | err_driver: | ||
384 | i2c_del_driver(&wm8728_i2c_driver); | ||
385 | return -ENODEV; | ||
386 | } | ||
387 | #endif | ||
388 | |||
389 | #if defined(CONFIG_SPI_MASTER) | 282 | #if defined(CONFIG_SPI_MASTER) |
390 | static int __devinit wm8728_spi_probe(struct spi_device *spi) | 283 | static int __devinit wm8728_spi_probe(struct spi_device *spi) |
391 | { | 284 | { |
392 | struct snd_soc_device *socdev = wm8728_socdev; | 285 | struct wm8728_priv *wm8728; |
393 | struct snd_soc_codec *codec = socdev->card->codec; | ||
394 | int ret; | 286 | int ret; |
395 | 287 | ||
396 | codec->control_data = spi; | 288 | wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); |
289 | if (wm8728 == NULL) | ||
290 | return -ENOMEM; | ||
397 | 291 | ||
398 | ret = wm8728_init(socdev, SND_SOC_SPI); | 292 | wm8728->control_data = spi; |
399 | if (ret < 0) | 293 | wm8728->control_type = SND_SOC_SPI; |
400 | dev_err(&spi->dev, "failed to initialise WM8728\n"); | 294 | spi_set_drvdata(spi, wm8728); |
401 | 295 | ||
296 | ret = snd_soc_register_codec(&spi->dev, | ||
297 | &soc_codec_dev_wm8728, &wm8728_dai, 1); | ||
298 | if (ret < 0) | ||
299 | kfree(wm8728); | ||
402 | return ret; | 300 | return ret; |
403 | } | 301 | } |
404 | 302 | ||
405 | static int __devexit wm8728_spi_remove(struct spi_device *spi) | 303 | static int __devexit wm8728_spi_remove(struct spi_device *spi) |
406 | { | 304 | { |
305 | snd_soc_unregister_codec(&spi->dev); | ||
306 | kfree(spi_get_drvdata(spi)); | ||
407 | return 0; | 307 | return 0; |
408 | } | 308 | } |
409 | 309 | ||
410 | static struct spi_driver wm8728_spi_driver = { | 310 | static struct spi_driver wm8728_spi_driver = { |
411 | .driver = { | 311 | .driver = { |
412 | .name = "wm8728", | 312 | .name = "wm8728-codec", |
413 | .bus = &spi_bus_type, | 313 | .bus = &spi_bus_type, |
414 | .owner = THIS_MODULE, | 314 | .owner = THIS_MODULE, |
415 | }, | 315 | }, |
@@ -418,85 +318,81 @@ static struct spi_driver wm8728_spi_driver = { | |||
418 | }; | 318 | }; |
419 | #endif /* CONFIG_SPI_MASTER */ | 319 | #endif /* CONFIG_SPI_MASTER */ |
420 | 320 | ||
421 | static int wm8728_probe(struct platform_device *pdev) | 321 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
322 | static __devinit int wm8728_i2c_probe(struct i2c_client *i2c, | ||
323 | const struct i2c_device_id *id) | ||
422 | { | 324 | { |
423 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 325 | struct wm8728_priv *wm8728; |
424 | struct wm8728_setup_data *setup; | 326 | int ret; |
425 | struct snd_soc_codec *codec; | ||
426 | int ret = 0; | ||
427 | 327 | ||
428 | setup = socdev->codec_data; | 328 | wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); |
429 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 329 | if (wm8728 == NULL) |
430 | if (codec == NULL) | ||
431 | return -ENOMEM; | 330 | return -ENOMEM; |
432 | 331 | ||
433 | socdev->card->codec = codec; | 332 | i2c_set_clientdata(i2c, wm8728); |
434 | mutex_init(&codec->mutex); | 333 | wm8728->control_data = i2c; |
435 | INIT_LIST_HEAD(&codec->dapm_widgets); | 334 | wm8728->control_type = SND_SOC_I2C; |
436 | INIT_LIST_HEAD(&codec->dapm_paths); | 335 | |
336 | ret = snd_soc_register_codec(&i2c->dev, | ||
337 | &soc_codec_dev_wm8728, &wm8728_dai, 1); | ||
338 | if (ret < 0) | ||
339 | kfree(wm8728); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | static __devexit int wm8728_i2c_remove(struct i2c_client *client) | ||
344 | { | ||
345 | snd_soc_unregister_codec(&client->dev); | ||
346 | kfree(i2c_get_clientdata(client)); | ||
347 | return 0; | ||
348 | } | ||
437 | 349 | ||
438 | wm8728_socdev = socdev; | 350 | static const struct i2c_device_id wm8728_i2c_id[] = { |
439 | ret = -ENODEV; | 351 | { "wm8728", 0 }, |
352 | { } | ||
353 | }; | ||
354 | MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); | ||
355 | |||
356 | static struct i2c_driver wm8728_i2c_driver = { | ||
357 | .driver = { | ||
358 | .name = "wm8728-codec", | ||
359 | .owner = THIS_MODULE, | ||
360 | }, | ||
361 | .probe = wm8728_i2c_probe, | ||
362 | .remove = __devexit_p(wm8728_i2c_remove), | ||
363 | .id_table = wm8728_i2c_id, | ||
364 | }; | ||
365 | #endif | ||
440 | 366 | ||
367 | static int __init wm8728_modinit(void) | ||
368 | { | ||
369 | int ret = 0; | ||
441 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 370 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
442 | if (setup->i2c_address) { | 371 | ret = i2c_add_driver(&wm8728_i2c_driver); |
443 | ret = wm8728_add_i2c_device(pdev, setup); | 372 | if (ret != 0) { |
373 | printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n", | ||
374 | ret); | ||
444 | } | 375 | } |
445 | #endif | 376 | #endif |
446 | #if defined(CONFIG_SPI_MASTER) | 377 | #if defined(CONFIG_SPI_MASTER) |
447 | if (setup->spi) { | 378 | ret = spi_register_driver(&wm8728_spi_driver); |
448 | ret = spi_register_driver(&wm8728_spi_driver); | 379 | if (ret != 0) { |
449 | if (ret != 0) | 380 | printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n", |
450 | printk(KERN_ERR "can't add spi driver"); | 381 | ret); |
451 | } | 382 | } |
452 | #endif | 383 | #endif |
453 | |||
454 | if (ret != 0) | ||
455 | kfree(codec); | ||
456 | |||
457 | return ret; | 384 | return ret; |
458 | } | 385 | } |
386 | module_init(wm8728_modinit); | ||
459 | 387 | ||
460 | /* power down chip */ | 388 | static void __exit wm8728_exit(void) |
461 | static int wm8728_remove(struct platform_device *pdev) | ||
462 | { | 389 | { |
463 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
464 | struct snd_soc_codec *codec = socdev->card->codec; | ||
465 | |||
466 | if (codec->control_data) | ||
467 | wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
468 | |||
469 | snd_soc_free_pcms(socdev); | ||
470 | snd_soc_dapm_free(socdev); | ||
471 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 390 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
472 | i2c_unregister_device(codec->control_data); | ||
473 | i2c_del_driver(&wm8728_i2c_driver); | 391 | i2c_del_driver(&wm8728_i2c_driver); |
474 | #endif | 392 | #endif |
475 | #if defined(CONFIG_SPI_MASTER) | 393 | #if defined(CONFIG_SPI_MASTER) |
476 | spi_unregister_driver(&wm8728_spi_driver); | 394 | spi_unregister_driver(&wm8728_spi_driver); |
477 | #endif | 395 | #endif |
478 | kfree(codec); | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | struct snd_soc_codec_device soc_codec_dev_wm8728 = { | ||
484 | .probe = wm8728_probe, | ||
485 | .remove = wm8728_remove, | ||
486 | .suspend = wm8728_suspend, | ||
487 | .resume = wm8728_resume, | ||
488 | }; | ||
489 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728); | ||
490 | |||
491 | static int __init wm8728_modinit(void) | ||
492 | { | ||
493 | return snd_soc_register_dai(&wm8728_dai); | ||
494 | } | ||
495 | module_init(wm8728_modinit); | ||
496 | |||
497 | static void __exit wm8728_exit(void) | ||
498 | { | ||
499 | snd_soc_unregister_dai(&wm8728_dai); | ||
500 | } | 396 | } |
501 | module_exit(wm8728_exit); | 397 | module_exit(wm8728_exit); |
502 | 398 | ||