1/* 2 * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec. 3 * 4 * Copyright 2012 Lucas Stach <dev@lynxeye.de> 5 * 6 * Partly based on code copyright/by: 7 * Copyright 2011,2012 Toradex Inc. 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * version 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 */ 19 20#include <linux/module.h> 21#include <linux/platform_device.h> 22#include <linux/slab.h> 23#include <linux/gpio.h> 24#include <linux/of_gpio.h> 25 26#include <sound/core.h> 27#include <sound/jack.h> 28#include <sound/pcm.h> 29#include <sound/pcm_params.h> 30#include <sound/soc.h> 31 32#include "tegra_asoc_utils.h" 33 34#define DRV_NAME "tegra-snd-wm9712" 35 36struct tegra_wm9712 { 37 struct platform_device *codec; 38 struct tegra_asoc_utils_data util_data; 39}; 40 41static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { 42 SND_SOC_DAPM_HP("Headphone", NULL), 43 SND_SOC_DAPM_LINE("LineIn", NULL), 44 SND_SOC_DAPM_MIC("Mic", NULL), 45}; 46 47static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) 48{ 49 return snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); 50} 51 52static struct snd_soc_dai_link tegra_wm9712_dai = { 53 .name = "AC97 HiFi", 54 .stream_name = "AC97 HiFi", 55 .codec_dai_name = "wm9712-hifi", 56 .codec_name = "wm9712-codec", 57 .init = tegra_wm9712_init, 58}; 59 60static struct snd_soc_card snd_soc_tegra_wm9712 = { 61 .name = "tegra-wm9712", 62 .owner = THIS_MODULE, 63 .dai_link = &tegra_wm9712_dai, 64 .num_links = 1, 65 66 .dapm_widgets = tegra_wm9712_dapm_widgets, 67 .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets), 68 .fully_routed = true, 69}; 70 71static int tegra_wm9712_driver_probe(struct platform_device *pdev) 72{ 73 struct device_node *np = pdev->dev.of_node; 74 struct snd_soc_card *card = &snd_soc_tegra_wm9712; 75 struct tegra_wm9712 *machine; 76 int ret; 77 78 machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712), 79 GFP_KERNEL); 80 if (!machine) { 81 dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n"); 82 return -ENOMEM; 83 } 84 85 card->dev = &pdev->dev; 86 platform_set_drvdata(pdev, card); 87 snd_soc_card_set_drvdata(card, machine); 88 89 machine->codec = platform_device_alloc("wm9712-codec", -1); 90 if (!machine->codec) { 91 dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n"); 92 return -ENOMEM; 93 } 94 95 ret = platform_device_add(machine->codec); 96 if (ret) 97 goto codec_put; 98 99 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 100 if (ret) 101 goto codec_unregister; 102 103 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 104 if (ret) 105 goto codec_unregister; 106 107 tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np, 108 "nvidia,ac97-controller", 0); 109 if (!tegra_wm9712_dai.cpu_of_node) { 110 dev_err(&pdev->dev, 111 "Property 'nvidia,ac97-controller' missing or invalid\n"); 112 ret = -EINVAL; 113 goto codec_unregister; 114 } 115 116 tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node; 117 118 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); 119 if (ret) 120 goto codec_unregister; 121 122 ret = tegra_asoc_utils_set_ac97_rate(&machine->util_data); 123 if (ret) 124 goto asoc_utils_fini; 125 126 ret = snd_soc_register_card(card); 127 if (ret) { 128 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 129 ret); 130 goto asoc_utils_fini; 131 } 132 133 return 0; 134 135asoc_utils_fini: 136 tegra_asoc_utils_fini(&machine->util_data); 137codec_unregister: 138 platform_device_del(machine->codec); 139codec_put: 140 platform_device_put(machine->codec); 141 return ret; 142} 143 144static int tegra_wm9712_driver_remove(struct platform_device *pdev) 145{ 146 struct snd_soc_card *card = platform_get_drvdata(pdev); 147 struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card); 148 149 snd_soc_unregister_card(card); 150 151 tegra_asoc_utils_fini(&machine->util_data); 152 153 platform_device_unregister(machine->codec); 154 155 return 0; 156} 157 158static const struct of_device_id tegra_wm9712_of_match[] = { 159 { .compatible = "nvidia,tegra-audio-wm9712", }, 160 {}, 161}; 162 163static struct platform_driver tegra_wm9712_driver = { 164 .driver = { 165 .name = DRV_NAME, 166 .pm = &snd_soc_pm_ops, 167 .of_match_table = tegra_wm9712_of_match, 168 }, 169 .probe = tegra_wm9712_driver_probe, 170 .remove = tegra_wm9712_driver_remove, 171}; 172module_platform_driver(tegra_wm9712_driver); 173 174MODULE_AUTHOR("Lucas Stach"); 175MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver"); 176MODULE_LICENSE("GPL v2"); 177MODULE_ALIAS("platform:" DRV_NAME); 178MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match); 179