aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/ssm2602.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/ssm2602.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/ssm2602.c')
-rw-r--r--sound/soc/codecs/ssm2602.c219
1 files changed, 59 insertions, 160 deletions
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index b47ed4f6ab20..67d8c044ca04 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -45,11 +45,11 @@
45 45
46#define SSM2602_VERSION "0.1" 46#define SSM2602_VERSION "0.1"
47 47
48struct snd_soc_codec_device soc_codec_dev_ssm2602;
49
50/* codec private data */ 48/* codec private data */
51struct ssm2602_priv { 49struct ssm2602_priv {
52 unsigned int sysclk; 50 unsigned int sysclk;
51 enum snd_soc_control_type control_type;
52 void *control_data;
53 struct snd_pcm_substream *master_substream; 53 struct snd_pcm_substream *master_substream;
54 struct snd_pcm_substream *slave_substream; 54 struct snd_pcm_substream *slave_substream;
55}; 55};
@@ -276,8 +276,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
276{ 276{
277 u16 srate; 277 u16 srate;
278 struct snd_soc_pcm_runtime *rtd = substream->private_data; 278 struct snd_soc_pcm_runtime *rtd = substream->private_data;
279 struct snd_soc_device *socdev = rtd->socdev; 279 struct snd_soc_codec *codec = rtd->codec;
280 struct snd_soc_codec *codec = socdev->card->codec;
281 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); 280 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
282 struct i2c_client *i2c = codec->control_data; 281 struct i2c_client *i2c = codec->control_data;
283 u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3; 282 u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
@@ -321,8 +320,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
321 struct snd_soc_dai *dai) 320 struct snd_soc_dai *dai)
322{ 321{
323 struct snd_soc_pcm_runtime *rtd = substream->private_data; 322 struct snd_soc_pcm_runtime *rtd = substream->private_data;
324 struct snd_soc_device *socdev = rtd->socdev; 323 struct snd_soc_codec *codec = rtd->codec;
325 struct snd_soc_codec *codec = socdev->card->codec;
326 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); 324 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
327 struct i2c_client *i2c = codec->control_data; 325 struct i2c_client *i2c = codec->control_data;
328 struct snd_pcm_runtime *master_runtime; 326 struct snd_pcm_runtime *master_runtime;
@@ -360,8 +358,7 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
360 struct snd_soc_dai *dai) 358 struct snd_soc_dai *dai)
361{ 359{
362 struct snd_soc_pcm_runtime *rtd = substream->private_data; 360 struct snd_soc_pcm_runtime *rtd = substream->private_data;
363 struct snd_soc_device *socdev = rtd->socdev; 361 struct snd_soc_codec *codec = rtd->codec;
364 struct snd_soc_codec *codec = socdev->card->codec;
365 /* set active */ 362 /* set active */
366 ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); 363 ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
367 364
@@ -372,8 +369,7 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
372 struct snd_soc_dai *dai) 369 struct snd_soc_dai *dai)
373{ 370{
374 struct snd_soc_pcm_runtime *rtd = substream->private_data; 371 struct snd_soc_pcm_runtime *rtd = substream->private_data;
375 struct snd_soc_device *socdev = rtd->socdev; 372 struct snd_soc_codec *codec = rtd->codec;
376 struct snd_soc_codec *codec = socdev->card->codec;
377 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); 373 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
378 374
379 /* deactivate */ 375 /* deactivate */
@@ -518,8 +514,8 @@ static struct snd_soc_dai_ops ssm2602_dai_ops = {
518 .set_fmt = ssm2602_set_dai_fmt, 514 .set_fmt = ssm2602_set_dai_fmt,
519}; 515};
520 516
521struct snd_soc_dai ssm2602_dai = { 517static struct snd_soc_dai_driver ssm2602_dai = {
522 .name = "SSM2602", 518 .name = "ssm2602-hifi",
523 .playback = { 519 .playback = {
524 .stream_name = "Playback", 520 .stream_name = "Playback",
525 .channels_min = 2, 521 .channels_min = 2,
@@ -534,21 +530,15 @@ struct snd_soc_dai ssm2602_dai = {
534 .formats = SSM2602_FORMATS,}, 530 .formats = SSM2602_FORMATS,},
535 .ops = &ssm2602_dai_ops, 531 .ops = &ssm2602_dai_ops,
536}; 532};
537EXPORT_SYMBOL_GPL(ssm2602_dai);
538 533
539static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state) 534static int ssm2602_suspend(struct snd_soc_codec *codec, pm_message_t state)
540{ 535{
541 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
542 struct snd_soc_codec *codec = socdev->card->codec;
543
544 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); 536 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
545 return 0; 537 return 0;
546} 538}
547 539
548static int ssm2602_resume(struct platform_device *pdev) 540static int ssm2602_resume(struct snd_soc_codec *codec)
549{ 541{
550 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
551 struct snd_soc_codec *codec = socdev->card->codec;
552 int i; 542 int i;
553 u8 data[2]; 543 u8 data[2];
554 u16 *cache = codec->reg_cache; 544 u16 *cache = codec->reg_cache;
@@ -563,36 +553,18 @@ static int ssm2602_resume(struct platform_device *pdev)
563 return 0; 553 return 0;
564} 554}
565 555
566/* 556static int ssm2602_probe(struct snd_soc_codec *codec)
567 * initialise the ssm2602 driver
568 * register the mixer and dsp interfaces with the kernel
569 */
570static int ssm2602_init(struct snd_soc_device *socdev)
571{ 557{
572 struct snd_soc_codec *codec = socdev->card->codec; 558 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
573 int reg, ret = 0; 559 int ret = 0, reg;
574 560
575 codec->name = "SSM2602"; 561 pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
576 codec->owner = THIS_MODULE; 562
577 codec->read = ssm2602_read_reg_cache; 563 codec->bias_level = SND_SOC_BIAS_OFF,
578 codec->write = ssm2602_write; 564 codec->control_data = ssm2602->control_data;
579 codec->set_bias_level = ssm2602_set_bias_level;
580 codec->dai = &ssm2602_dai;
581 codec->num_dai = 1;
582 codec->reg_cache_size = sizeof(ssm2602_reg);
583 codec->reg_cache = kmemdup(ssm2602_reg, sizeof(ssm2602_reg),
584 GFP_KERNEL);
585 if (codec->reg_cache == NULL)
586 return -ENOMEM;
587 565
588 ssm2602_reset(codec); 566 ssm2602_reset(codec);
589 567
590 /* register pcms */
591 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
592 if (ret < 0) {
593 pr_err("ssm2602: failed to create pcms\n");
594 goto pcm_err;
595 }
596 /*power on device*/ 568 /*power on device*/
597 ssm2602_write(codec, SSM2602_ACTIVE, 0); 569 ssm2602_write(codec, SSM2602_ACTIVE, 0);
598 /* set the update bits */ 570 /* set the update bits */
@@ -614,13 +586,27 @@ static int ssm2602_init(struct snd_soc_device *socdev)
614 ssm2602_add_widgets(codec); 586 ssm2602_add_widgets(codec);
615 587
616 return ret; 588 return ret;
589}
617 590
618pcm_err: 591/* remove everything here */
619 kfree(codec->reg_cache); 592static int ssm2602_remove(struct snd_soc_codec *codec)
620 return ret; 593{
594 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
595 return 0;
621} 596}
622 597
623static struct snd_soc_device *ssm2602_socdev; 598static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
599 .probe = ssm2602_probe,
600 .remove = ssm2602_remove,
601 .suspend = ssm2602_suspend,
602 .resume = ssm2602_resume,
603 .read = ssm2602_read_reg_cache,
604 .write = ssm2602_write,
605 .set_bias_level = ssm2602_set_bias_level,
606 .reg_cache_size = sizeof(ssm2602_reg),
607 .reg_word_size = sizeof(u16),
608 .reg_cache_default = ssm2602_reg,
609};
624 610
625#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 611#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
626/* 612/*
@@ -632,24 +618,28 @@ static struct snd_soc_device *ssm2602_socdev;
632static int ssm2602_i2c_probe(struct i2c_client *i2c, 618static int ssm2602_i2c_probe(struct i2c_client *i2c,
633 const struct i2c_device_id *id) 619 const struct i2c_device_id *id)
634{ 620{
635 struct snd_soc_device *socdev = ssm2602_socdev; 621 struct ssm2602_priv *ssm2602;
636 struct snd_soc_codec *codec = socdev->card->codec;
637 int ret; 622 int ret;
638 623
639 i2c_set_clientdata(i2c, codec); 624 ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
640 codec->control_data = i2c; 625 if (ssm2602 == NULL)
626 return -ENOMEM;
641 627
642 ret = ssm2602_init(socdev); 628 i2c_set_clientdata(i2c, ssm2602);
643 if (ret < 0) 629 ssm2602->control_data = i2c;
644 pr_err("failed to initialise SSM2602\n"); 630 ssm2602->control_type = SND_SOC_I2C;
645 631
632 ret = snd_soc_register_codec(&i2c->dev,
633 &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
634 if (ret < 0)
635 kfree(ssm2602);
646 return ret; 636 return ret;
647} 637}
648 638
649static int ssm2602_i2c_remove(struct i2c_client *client) 639static int ssm2602_i2c_remove(struct i2c_client *client)
650{ 640{
651 struct snd_soc_codec *codec = i2c_get_clientdata(client); 641 snd_soc_unregister_codec(&client->dev);
652 kfree(codec->reg_cache); 642 kfree(i2c_get_clientdata(client));
653 return 0; 643 return 0;
654} 644}
655 645
@@ -658,130 +648,39 @@ static const struct i2c_device_id ssm2602_i2c_id[] = {
658 { } 648 { }
659}; 649};
660MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); 650MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
651
661/* corgi i2c codec control layer */ 652/* corgi i2c codec control layer */
662static struct i2c_driver ssm2602_i2c_driver = { 653static struct i2c_driver ssm2602_i2c_driver = {
663 .driver = { 654 .driver = {
664 .name = "SSM2602 I2C Codec", 655 .name = "ssm2602-codec",
665 .owner = THIS_MODULE, 656 .owner = THIS_MODULE,
666 }, 657 },
667 .probe = ssm2602_i2c_probe, 658 .probe = ssm2602_i2c_probe,
668 .remove = ssm2602_i2c_remove, 659 .remove = ssm2602_i2c_remove,
669 .id_table = ssm2602_i2c_id, 660 .id_table = ssm2602_i2c_id,
670}; 661};
671
672static int ssm2602_add_i2c_device(struct platform_device *pdev,
673 const struct ssm2602_setup_data *setup)
674{
675 struct i2c_board_info info;
676 struct i2c_adapter *adapter;
677 struct i2c_client *client;
678 int ret;
679
680 ret = i2c_add_driver(&ssm2602_i2c_driver);
681 if (ret != 0) {
682 dev_err(&pdev->dev, "can't add i2c driver\n");
683 return ret;
684 }
685 memset(&info, 0, sizeof(struct i2c_board_info));
686 info.addr = setup->i2c_address;
687 strlcpy(info.type, "ssm2602", I2C_NAME_SIZE);
688 adapter = i2c_get_adapter(setup->i2c_bus);
689 if (!adapter) {
690 dev_err(&pdev->dev, "can't get i2c adapter %d\n",
691 setup->i2c_bus);
692 goto err_driver;
693 }
694 client = i2c_new_device(adapter, &info);
695 i2c_put_adapter(adapter);
696 if (!client) {
697 dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
698 (unsigned int)info.addr);
699 goto err_driver;
700 }
701 return 0;
702err_driver:
703 i2c_del_driver(&ssm2602_i2c_driver);
704 return -ENODEV;
705}
706#endif 662#endif
707 663
708static int ssm2602_probe(struct platform_device *pdev) 664
665static int __init ssm2602_modinit(void)
709{ 666{
710 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
711 struct ssm2602_setup_data *setup;
712 struct snd_soc_codec *codec;
713 struct ssm2602_priv *ssm2602;
714 int ret = 0; 667 int ret = 0;
715
716 pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
717
718 setup = socdev->codec_data;
719 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
720 if (codec == NULL)
721 return -ENOMEM;
722
723 ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
724 if (ssm2602 == NULL) {
725 kfree(codec);
726 return -ENOMEM;
727 }
728
729 snd_soc_codec_set_drvdata(codec, ssm2602);
730 socdev->card->codec = codec;
731 mutex_init(&codec->mutex);
732 INIT_LIST_HEAD(&codec->dapm_widgets);
733 INIT_LIST_HEAD(&codec->dapm_paths);
734
735 ssm2602_socdev = socdev;
736#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 668#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
737 if (setup->i2c_address) { 669 ret = i2c_add_driver(&ssm2602_i2c_driver);
738 codec->hw_write = (hw_write_t)i2c_master_send; 670 if (ret != 0) {
739 ret = ssm2602_add_i2c_device(pdev, setup); 671 printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n",
672 ret);
740 } 673 }
741#else
742 /* other interfaces */
743#endif 674#endif
744 return ret; 675 return ret;
745} 676}
677module_init(ssm2602_modinit);
746 678
747/* remove everything here */ 679static void __exit ssm2602_exit(void)
748static int ssm2602_remove(struct platform_device *pdev)
749{ 680{
750 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
751 struct snd_soc_codec *codec = socdev->card->codec;
752
753 if (codec->control_data)
754 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
755
756 snd_soc_free_pcms(socdev);
757 snd_soc_dapm_free(socdev);
758#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 681#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
759 i2c_unregister_device(codec->control_data);
760 i2c_del_driver(&ssm2602_i2c_driver); 682 i2c_del_driver(&ssm2602_i2c_driver);
761#endif 683#endif
762 kfree(snd_soc_codec_get_drvdata(codec));
763 kfree(codec);
764
765 return 0;
766}
767
768struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
769 .probe = ssm2602_probe,
770 .remove = ssm2602_remove,
771 .suspend = ssm2602_suspend,
772 .resume = ssm2602_resume,
773};
774EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
775
776static int __init ssm2602_modinit(void)
777{
778 return snd_soc_register_dai(&ssm2602_dai);
779}
780module_init(ssm2602_modinit);
781
782static void __exit ssm2602_exit(void)
783{
784 snd_soc_unregister_dai(&ssm2602_dai);
785} 684}
786module_exit(ssm2602_exit); 685module_exit(ssm2602_exit);
787 686