aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8400.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-03-17 16:15:21 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-08-12 09:00:00 -0400
commitf0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch)
treef6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/codecs/wm8400.c
parentbda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff)
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one CODEC and more than one platform DMA controller. This is achieved by dividing some current ASoC structures that contain both driver data and device data into structures that only either contain device data or driver data. i.e. struct snd_soc_codec ---> struct snd_soc_codec (device data) +-> struct snd_soc_codec_driver (driver data) struct snd_soc_platform ---> struct snd_soc_platform (device data) +-> struct snd_soc_platform_driver (driver data) struct snd_soc_dai ---> struct snd_soc_dai (device data) +-> struct snd_soc_dai_driver (driver data) struct snd_soc_device ---> deleted This now allows ASoC to be more tightly aligned with the Linux driver model and also means that every ASoC codec, platform and (platform) DAI is a kernel device. ASoC component private data is now stored as device private data. The ASoC sound card struct snd_soc_card has also been updated to store lists of it's components rather than a pointer to a codec and platform. The PCM runtime struct soc_pcm_runtime now has pointers to all its components. This patch adds DAPM support for ASoC multi-component and removes struct snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec or runtime PCM level basis rather than using snd_soc_socdev. Other notable multi-component changes:- * Stream operations now de-reference less structures. * close_delayed work() now runs on a DAI basis rather than looping all DAIs in a card. * PM suspend()/resume() operations can now handle N CODECs and Platforms per sound card. * Added soc_bind_dai_link() to bind the component devices to the sound card. * Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove DAI link components. * sysfs entries can now be registered per component per card. * snd_soc_new_pcms() functionailty rolled into dai_link_probe(). * snd_soc_register_codec() now does all the codec list and mutex init. This patch changes the probe() and remove() of the CODEC drivers as follows:- o Make CODEC driver a platform driver o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core. o Removed all static codec pointers (drivers now support > 1 codec dev) o snd_soc_register_pcms() now done by core. o snd_soc_register_dai() folded into snd_soc_register_codec(). CS4270 portions: Acked-by: Timur Tabi <timur@freescale.com> Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Jassi Brar <jassi.brar@samsung.com> Signed-off-by: Seungwhan Youn <sw.youn@samsung.com> MPC8610 and PPC fixes. Signed-off-by: Timur Tabi <timur@freescale.com> i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> CC: Tony Lindgren <tony@atomide.com> CC: Nicolas Ferre <nicolas.ferre@atmel.com> CC: Kevin Hilman <khilman@deeprootsystems.com> CC: Sascha Hauer <s.hauer@pengutronix.de> CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp> CC: Kuninori Morimoto <morimoto.kuninori@renesas.com> CC: Daniel Gloeckner <dg@emlix.com> CC: Manuel Lauss <mano@roarinelk.homelinux.net> CC: Mike Frysinger <vapier.adi@gmail.com> CC: Arnaud Patard <apatard@mandriva.com> CC: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8400.c')
-rw-r--r--sound/soc/codecs/wm8400.c181
1 files changed, 49 insertions, 132 deletions
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 8f294066b0ed..850299786e02 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -65,7 +65,7 @@ static struct regulator_bulk_data power[] = {
65 65
66/* codec private data */ 66/* codec private data */
67struct wm8400_priv { 67struct wm8400_priv {
68 struct snd_soc_codec codec; 68 struct snd_soc_codec *codec;
69 struct wm8400 *wm8400; 69 struct wm8400 *wm8400;
70 u16 fake_register; 70 u16 fake_register;
71 unsigned int sysclk; 71 unsigned int sysclk;
@@ -1163,8 +1163,7 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream,
1163 struct snd_soc_dai *dai) 1163 struct snd_soc_dai *dai)
1164{ 1164{
1165 struct snd_soc_pcm_runtime *rtd = substream->private_data; 1165 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1166 struct snd_soc_device *socdev = rtd->socdev; 1166 struct snd_soc_codec *codec = rtd->codec;
1167 struct snd_soc_codec *codec = socdev->card->codec;
1168 u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); 1167 u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
1169 1168
1170 audio1 &= ~WM8400_AIF_WL_MASK; 1169 audio1 &= ~WM8400_AIF_WL_MASK;
@@ -1332,10 +1331,9 @@ static struct snd_soc_dai_ops wm8400_dai_ops = {
1332 * 1. ADC/DAC on Primary Interface 1331 * 1. ADC/DAC on Primary Interface
1333 * 2. ADC on Primary Interface/DAC on secondary 1332 * 2. ADC on Primary Interface/DAC on secondary
1334 */ 1333 */
1335struct snd_soc_dai wm8400_dai = { 1334static struct snd_soc_dai_driver wm8400_dai = {
1336/* ADC/DAC on primary */ 1335/* ADC/DAC on primary */
1337 .name = "WM8400 ADC/DAC Primary", 1336 .name = "wm8400-hifi",
1338 .id = 1,
1339 .playback = { 1337 .playback = {
1340 .stream_name = "Playback", 1338 .stream_name = "Playback",
1341 .channels_min = 1, 1339 .channels_min = 1,
@@ -1352,147 +1350,53 @@ struct snd_soc_dai wm8400_dai = {
1352 }, 1350 },
1353 .ops = &wm8400_dai_ops, 1351 .ops = &wm8400_dai_ops,
1354}; 1352};
1355EXPORT_SYMBOL_GPL(wm8400_dai);
1356 1353
1357static int wm8400_suspend(struct platform_device *pdev, pm_message_t state) 1354static int wm8400_suspend(struct snd_soc_codec *codec, pm_message_t state)
1358{ 1355{
1359 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1360 struct snd_soc_codec *codec = socdev->card->codec;
1361
1362 wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); 1356 wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
1363 1357
1364 return 0; 1358 return 0;
1365} 1359}
1366 1360
1367static int wm8400_resume(struct platform_device *pdev) 1361static int wm8400_resume(struct snd_soc_codec *codec)
1368{ 1362{
1369 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1370 struct snd_soc_codec *codec = socdev->card->codec;
1371
1372 wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1363 wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1373 1364
1374 return 0; 1365 return 0;
1375} 1366}
1376 1367
1377static struct snd_soc_codec *wm8400_codec;
1378
1379static int wm8400_probe(struct platform_device *pdev)
1380{
1381 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1382 struct snd_soc_codec *codec;
1383 int ret;
1384
1385 if (!wm8400_codec) {
1386 dev_err(&pdev->dev, "wm8400 not yet discovered\n");
1387 return -ENODEV;
1388 }
1389 codec = wm8400_codec;
1390
1391 socdev->card->codec = codec;
1392
1393 /* register pcms */
1394 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1395 if (ret < 0) {
1396 dev_err(&pdev->dev, "failed to create pcms\n");
1397 goto pcm_err;
1398 }
1399
1400 wm8400_add_controls(codec);
1401 wm8400_add_widgets(codec);
1402
1403pcm_err:
1404 return ret;
1405}
1406
1407/* power down chip */
1408static int wm8400_remove(struct platform_device *pdev)
1409{
1410 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1411
1412 snd_soc_free_pcms(socdev);
1413 snd_soc_dapm_free(socdev);
1414
1415 return 0;
1416}
1417
1418struct snd_soc_codec_device soc_codec_dev_wm8400 = {
1419 .probe = wm8400_probe,
1420 .remove = wm8400_remove,
1421 .suspend = wm8400_suspend,
1422 .resume = wm8400_resume,
1423};
1424
1425static void wm8400_probe_deferred(struct work_struct *work) 1368static void wm8400_probe_deferred(struct work_struct *work)
1426{ 1369{
1427 struct wm8400_priv *priv = container_of(work, struct wm8400_priv, 1370 struct wm8400_priv *priv = container_of(work, struct wm8400_priv,
1428 work); 1371 work);
1429 struct snd_soc_codec *codec = &priv->codec; 1372 struct snd_soc_codec *codec = priv->codec;
1430 int ret;
1431 1373
1432 /* charge output caps */ 1374 /* charge output caps */
1433 wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1375 wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1434
1435 /* We're done, tell the subsystem. */
1436 ret = snd_soc_register_codec(codec);
1437 if (ret != 0) {
1438 dev_err(priv->wm8400->dev,
1439 "Failed to register codec: %d\n", ret);
1440 goto err;
1441 }
1442
1443 ret = snd_soc_register_dai(&wm8400_dai);
1444 if (ret != 0) {
1445 dev_err(priv->wm8400->dev,
1446 "Failed to register DAI: %d\n", ret);
1447 goto err_codec;
1448 }
1449
1450 return;
1451
1452err_codec:
1453 snd_soc_unregister_codec(codec);
1454err:
1455 wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
1456} 1376}
1457 1377
1458static int wm8400_codec_probe(struct platform_device *dev) 1378static int wm8400_codec_probe(struct snd_soc_codec *codec)
1459{ 1379{
1380 struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
1460 struct wm8400_priv *priv; 1381 struct wm8400_priv *priv;
1461 int ret; 1382 int ret;
1462 u16 reg; 1383 u16 reg;
1463 struct snd_soc_codec *codec;
1464 1384
1465 priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL); 1385 priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL);
1466 if (priv == NULL) 1386 if (priv == NULL)
1467 return -ENOMEM; 1387 return -ENOMEM;
1468 1388
1469 codec = &priv->codec;
1470 snd_soc_codec_set_drvdata(codec, priv); 1389 snd_soc_codec_set_drvdata(codec, priv);
1471 codec->control_data = dev_get_drvdata(&dev->dev); 1390 codec->control_data = priv->wm8400 = wm8400;
1472 priv->wm8400 = dev_get_drvdata(&dev->dev); 1391 priv->codec = codec;
1473 1392
1474 ret = regulator_bulk_get(priv->wm8400->dev, 1393 ret = regulator_bulk_get(wm8400->dev,
1475 ARRAY_SIZE(power), &power[0]); 1394 ARRAY_SIZE(power), &power[0]);
1476 if (ret != 0) { 1395 if (ret != 0) {
1477 dev_err(&dev->dev, "Failed to get regulators: %d\n", ret); 1396 dev_err(codec->dev, "Failed to get regulators: %d\n", ret);
1478 goto err; 1397 goto err;
1479 } 1398 }
1480 1399
1481 codec->dev = &dev->dev;
1482 wm8400_dai.dev = &dev->dev;
1483
1484 codec->name = "WM8400";
1485 codec->owner = THIS_MODULE;
1486 codec->read = wm8400_read;
1487 codec->write = wm8400_write;
1488 codec->bias_level = SND_SOC_BIAS_OFF;
1489 codec->set_bias_level = wm8400_set_bias_level;
1490 codec->dai = &wm8400_dai;
1491 codec->num_dai = 1;
1492 codec->reg_cache_size = WM8400_REGISTER_COUNT;
1493 mutex_init(&codec->mutex);
1494 INIT_LIST_HEAD(&codec->dapm_widgets);
1495 INIT_LIST_HEAD(&codec->dapm_paths);
1496 INIT_WORK(&priv->work, wm8400_probe_deferred); 1400 INIT_WORK(&priv->work, wm8400_probe_deferred);
1497 1401
1498 wm8400_codec_reset(codec); 1402 wm8400_codec_reset(codec);
@@ -1511,65 +1415,78 @@ static int wm8400_codec_probe(struct platform_device *dev)
1511 wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); 1415 wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
1512 wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); 1416 wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
1513 1417
1514 wm8400_codec = codec;
1515
1516 if (!schedule_work(&priv->work)) { 1418 if (!schedule_work(&priv->work)) {
1517 ret = -EINVAL; 1419 ret = -EINVAL;
1518 goto err_regulator; 1420 goto err_regulator;
1519 } 1421 }
1520 1422 wm8400_add_controls(codec);
1423 wm8400_add_widgets(codec);
1521 return 0; 1424 return 0;
1522 1425
1523err_regulator: 1426err_regulator:
1524 wm8400_codec = NULL;
1525 regulator_bulk_free(ARRAY_SIZE(power), power); 1427 regulator_bulk_free(ARRAY_SIZE(power), power);
1526err: 1428err:
1527 kfree(priv); 1429 kfree(priv);
1528 return ret; 1430 return ret;
1529} 1431}
1530 1432
1531static int __exit wm8400_codec_remove(struct platform_device *dev) 1433static int wm8400_codec_remove(struct snd_soc_codec *codec)
1532{ 1434{
1533 struct wm8400_priv *priv = snd_soc_codec_get_drvdata(wm8400_codec); 1435 struct wm8400_priv *priv = snd_soc_codec_get_drvdata(codec);
1534 u16 reg; 1436 u16 reg;
1535 1437
1536 snd_soc_unregister_dai(&wm8400_dai); 1438 reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
1537 snd_soc_unregister_codec(wm8400_codec); 1439 wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
1538
1539 reg = wm8400_read(wm8400_codec, WM8400_POWER_MANAGEMENT_1);
1540 wm8400_write(wm8400_codec, WM8400_POWER_MANAGEMENT_1,
1541 reg & (~WM8400_CODEC_ENA)); 1440 reg & (~WM8400_CODEC_ENA));
1542 1441
1543 regulator_bulk_free(ARRAY_SIZE(power), power); 1442 regulator_bulk_free(ARRAY_SIZE(power), power);
1544 kfree(priv); 1443 kfree(priv);
1545 1444
1546 wm8400_codec = NULL; 1445 return 0;
1446}
1447
1448static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
1449 .probe = wm8400_codec_probe,
1450 .remove = wm8400_codec_remove,
1451 .suspend = wm8400_suspend,
1452 .resume = wm8400_resume,
1453 .read = wm8400_read,
1454 .write = wm8400_write,
1455 .set_bias_level = wm8400_set_bias_level,
1456};
1457
1458static int __devinit wm8400_probe(struct platform_device *pdev)
1459{
1460 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8400,
1461 &wm8400_dai, 1);
1462}
1547 1463
1464static int __devexit wm8400_remove(struct platform_device *pdev)
1465{
1466 snd_soc_unregister_codec(&pdev->dev);
1548 return 0; 1467 return 0;
1549} 1468}
1550 1469
1551static struct platform_driver wm8400_codec_driver = { 1470static struct platform_driver wm8400_codec_driver = {
1552 .driver = { 1471 .driver = {
1553 .name = "wm8400-codec", 1472 .name = "wm8400-codec",
1554 .owner = THIS_MODULE, 1473 .owner = THIS_MODULE,
1555 }, 1474 },
1556 .probe = wm8400_codec_probe, 1475 .probe = wm8400_probe,
1557 .remove = __exit_p(wm8400_codec_remove), 1476 .remove = __devexit_p(wm8400_remove),
1558}; 1477};
1559 1478
1560static int __init wm8400_codec_init(void) 1479static __init int wm8400_init(void)
1561{ 1480{
1562 return platform_driver_register(&wm8400_codec_driver); 1481 return platform_driver_register(&wm8400_codec_driver);
1563} 1482}
1564module_init(wm8400_codec_init); 1483module_init(wm8400_init);
1565 1484
1566static void __exit wm8400_codec_exit(void) 1485static __exit void wm8400_exit(void)
1567{ 1486{
1568 platform_driver_unregister(&wm8400_codec_driver); 1487 platform_driver_unregister(&wm8400_codec_driver);
1569} 1488}
1570module_exit(wm8400_codec_exit); 1489module_exit(wm8400_exit);
1571
1572EXPORT_SYMBOL_GPL(soc_codec_dev_wm8400);
1573 1490
1574MODULE_DESCRIPTION("ASoC WM8400 driver"); 1491MODULE_DESCRIPTION("ASoC WM8400 driver");
1575MODULE_AUTHOR("Mark Brown"); 1492MODULE_AUTHOR("Mark Brown");