1/* 2 * afeb9260.c -- SoC audio for AFEB9260 3 * 4 * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * version 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18 * 02110-1301 USA 19 * 20 */ 21 22#include <linux/module.h> 23#include <linux/moduleparam.h> 24#include <linux/kernel.h> 25#include <linux/clk.h> 26#include <linux/platform_device.h> 27 28#include <linux/atmel-ssc.h> 29#include <sound/core.h> 30#include <sound/pcm.h> 31#include <sound/pcm_params.h> 32#include <sound/soc.h> 33#include <sound/soc-dapm.h> 34 35#include <asm/mach-types.h> 36#include <mach/hardware.h> 37#include <linux/gpio.h> 38 39#include "../codecs/tlv320aic23.h" 40#include "atmel-pcm.h" 41#include "atmel_ssc_dai.h" 42 43#define CODEC_CLOCK 12000000 44 45static int afeb9260_hw_params(struct snd_pcm_substream *substream, 46 struct snd_pcm_hw_params *params) 47{ 48 struct snd_soc_pcm_runtime *rtd = substream->private_data; 49 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 50 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 51 int err; 52 53 /* Set codec DAI configuration */ 54 err = snd_soc_dai_set_fmt(codec_dai, 55 SND_SOC_DAIFMT_I2S| 56 SND_SOC_DAIFMT_NB_IF | 57 SND_SOC_DAIFMT_CBM_CFM); 58 if (err < 0) { 59 printk(KERN_ERR "can't set codec DAI configuration\n"); 60 return err; 61 } 62 63 /* Set cpu DAI configuration */ 64 err = snd_soc_dai_set_fmt(cpu_dai, 65 SND_SOC_DAIFMT_I2S | 66 SND_SOC_DAIFMT_NB_IF | 67 SND_SOC_DAIFMT_CBM_CFM); 68 if (err < 0) { 69 printk(KERN_ERR "can't set cpu DAI configuration\n"); 70 return err; 71 } 72 73 /* Set the codec system clock for DAC and ADC */ 74 err = 75 snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); 76 77 if (err < 0) { 78 printk(KERN_ERR "can't set codec system clock\n"); 79 return err; 80 } 81 82 return err; 83} 84 85static struct snd_soc_ops afeb9260_ops = { 86 .hw_params = afeb9260_hw_params, 87}; 88 89static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { 90 SND_SOC_DAPM_HP("Headphone Jack", NULL), 91 SND_SOC_DAPM_LINE("Line In", NULL), 92 SND_SOC_DAPM_MIC("Mic Jack", NULL), 93}; 94 95static const struct snd_soc_dapm_route audio_map[] = { 96 {"Headphone Jack", NULL, "LHPOUT"}, 97 {"Headphone Jack", NULL, "RHPOUT"}, 98 99 {"LLINEIN", NULL, "Line In"}, 100 {"RLINEIN", NULL, "Line In"}, 101 102 {"MICIN", NULL, "Mic Jack"}, 103}; 104 105static int afeb9260_tlv320aic23_init(struct snd_soc_codec *codec) 106{ 107 108 /* Add afeb9260 specific widgets */ 109 snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, 110 ARRAY_SIZE(tlv320aic23_dapm_widgets)); 111 112 /* Set up afeb9260 specific audio path audio_map */ 113 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 114 115 snd_soc_dapm_enable_pin(codec, "Headphone Jack"); 116 snd_soc_dapm_enable_pin(codec, "Line In"); 117 snd_soc_dapm_enable_pin(codec, "Mic Jack"); 118 119 snd_soc_dapm_sync(codec); 120 121 return 0; 122} 123 124/* Digital audio interface glue - connects codec <--> CPU */ 125static struct snd_soc_dai_link afeb9260_dai = { 126 .name = "TLV320AIC23", 127 .stream_name = "AIC23", 128 .cpu_dai = &atmel_ssc_dai[0], 129 .codec_dai = &tlv320aic23_dai, 130 .init = afeb9260_tlv320aic23_init, 131 .ops = &afeb9260_ops, 132}; 133 134/* Audio machine driver */ 135static struct snd_soc_card snd_soc_machine_afeb9260 = { 136 .name = "AFEB9260", 137 .platform = &atmel_soc_platform, 138 .dai_link = &afeb9260_dai, 139 .num_links = 1, 140}; 141 142/* Audio subsystem */ 143static struct snd_soc_device afeb9260_snd_devdata = { 144 .card = &snd_soc_machine_afeb9260, 145 .codec_dev = &soc_codec_dev_tlv320aic23, 146}; 147 148static struct platform_device *afeb9260_snd_device; 149 150static int __init afeb9260_soc_init(void) 151{ 152 int err; 153 struct device *dev; 154 struct atmel_ssc_info *ssc_p = afeb9260_dai.cpu_dai->private_data; 155 struct ssc_device *ssc = NULL; 156 157 if (!(machine_is_afeb9260())) 158 return -ENODEV; 159 160 ssc = ssc_request(0); 161 if (IS_ERR(ssc)) { 162 printk(KERN_ERR "ASoC: Failed to request SSC 0\n"); 163 err = PTR_ERR(ssc); 164 ssc = NULL; 165 goto err_ssc; 166 } 167 ssc_p->ssc = ssc; 168 169 afeb9260_snd_device = platform_device_alloc("soc-audio", -1); 170 if (!afeb9260_snd_device) { 171 printk(KERN_ERR "ASoC: Platform device allocation failed\n"); 172 return -ENOMEM; 173 } 174 175 platform_set_drvdata(afeb9260_snd_device, &afeb9260_snd_devdata); 176 afeb9260_snd_devdata.dev = &afeb9260_snd_device->dev; 177 err = platform_device_add(afeb9260_snd_device); 178 if (err) 179 goto err1; 180 181 dev = &afeb9260_snd_device->dev; 182 183 return 0; 184err1: 185 platform_device_del(afeb9260_snd_device); 186 platform_device_put(afeb9260_snd_device); 187err_ssc: 188 return err; 189 190} 191 192static void __exit afeb9260_soc_exit(void) 193{ 194 platform_device_unregister(afeb9260_snd_device); 195} 196 197module_init(afeb9260_soc_init); 198module_exit(afeb9260_soc_exit); 199 200MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>"); 201MODULE_DESCRIPTION("ALSA SoC for AFEB9260"); 202MODULE_LICENSE("GPL"); 203