1/* 2 * ASoC driver for Stretch s6105 IP camera platform 3 * 4 * Author: Daniel Gloeckner, <dg@emlix.com> 5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/module.h> 13#include <linux/moduleparam.h> 14#include <linux/timer.h> 15#include <linux/interrupt.h> 16#include <linux/platform_device.h> 17#include <linux/i2c.h> 18#include <sound/core.h> 19#include <sound/pcm.h> 20#include <sound/soc.h> 21#include <sound/soc-dapm.h> 22 23#include <variant/dmac.h> 24 25#include "../codecs/tlv320aic3x.h" 26#include "s6000-pcm.h" 27#include "s6000-i2s.h" 28 29#define S6105_CAM_CODEC_CLOCK 12288000 30 31static int s6105_hw_params(struct snd_pcm_substream *substream, 32 struct snd_pcm_hw_params *params) 33{ 34 struct snd_soc_pcm_runtime *rtd = substream->private_data; 35 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 36 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 37 int ret = 0; 38 39 /* set codec DAI configuration */ 40 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 41 SND_SOC_DAIFMT_CBM_CFM); 42 if (ret < 0) 43 return ret; 44 45 /* set cpu DAI configuration */ 46 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM | 47 SND_SOC_DAIFMT_NB_NF); 48 if (ret < 0) 49 return ret; 50 51 /* set the codec system clock */ 52 ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK, 53 SND_SOC_CLOCK_OUT); 54 if (ret < 0) 55 return ret; 56 57 return 0; 58} 59 60static struct snd_soc_ops s6105_ops = { 61 .hw_params = s6105_hw_params, 62}; 63 64/* s6105 machine dapm widgets */ 65static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { 66 SND_SOC_DAPM_LINE("Audio Out Differential", NULL), 67 SND_SOC_DAPM_LINE("Audio Out Stereo", NULL), 68 SND_SOC_DAPM_LINE("Audio In", NULL), 69}; 70 71/* s6105 machine audio_mapnections to the codec pins */ 72static const struct snd_soc_dapm_route audio_map[] = { 73 /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */ 74 {"Audio Out Differential", NULL, "HPLOUT"}, 75 {"Audio Out Differential", NULL, "HPLCOM"}, 76 {"Audio Out Stereo", NULL, "HPLOUT"}, 77 {"Audio Out Stereo", NULL, "HPROUT"}, 78 79 /* Audio In connected to LINE1L, LINE1R */ 80 {"LINE1L", NULL, "Audio In"}, 81 {"LINE1R", NULL, "Audio In"}, 82}; 83 84static int output_type_info(struct snd_kcontrol *kcontrol, 85 struct snd_ctl_elem_info *uinfo) 86{ 87 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 88 uinfo->count = 1; 89 uinfo->value.enumerated.items = 2; 90 if (uinfo->value.enumerated.item) { 91 uinfo->value.enumerated.item = 1; 92 strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT"); 93 } else { 94 strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM"); 95 } 96 return 0; 97} 98 99static int output_type_get(struct snd_kcontrol *kcontrol, 100 struct snd_ctl_elem_value *ucontrol) 101{ 102 ucontrol->value.enumerated.item[0] = kcontrol->private_value; 103 return 0; 104} 105 106static int output_type_put(struct snd_kcontrol *kcontrol, 107 struct snd_ctl_elem_value *ucontrol) 108{ 109 struct snd_soc_codec *codec = kcontrol->private_data; 110 unsigned int val = (ucontrol->value.enumerated.item[0] != 0); 111 char *differential = "Audio Out Differential"; 112 char *stereo = "Audio Out Stereo"; 113 114 if (kcontrol->private_value == val) 115 return 0; 116 kcontrol->private_value = val; 117 snd_soc_dapm_disable_pin(codec, val ? differential : stereo); 118 snd_soc_dapm_sync(codec); 119 snd_soc_dapm_enable_pin(codec, val ? stereo : differential); 120 snd_soc_dapm_sync(codec); 121 122 return 1; 123} 124 125static const struct snd_kcontrol_new audio_out_mux = { 126 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 127 .name = "Master Output Mux", 128 .index = 0, 129 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 130 .info = output_type_info, 131 .get = output_type_get, 132 .put = output_type_put, 133 .private_value = 1 /* default to stereo */ 134}; 135 136/* Logic for a aic3x as connected on the s6105 ip camera ref design */ 137static int s6105_aic3x_init(struct snd_soc_codec *codec) 138{ 139 /* Add s6105 specific widgets */ 140 snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, 141 ARRAY_SIZE(aic3x_dapm_widgets)); 142 143 /* Set up s6105 specific audio path audio_map */ 144 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 145 146 /* not present */ 147 snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); 148 snd_soc_dapm_nc_pin(codec, "LINE2L"); 149 snd_soc_dapm_nc_pin(codec, "LINE2R"); 150 151 /* not connected */ 152 snd_soc_dapm_nc_pin(codec, "MIC3L"); /* LINE2L on this chip */ 153 snd_soc_dapm_nc_pin(codec, "MIC3R"); /* LINE2R on this chip */ 154 snd_soc_dapm_nc_pin(codec, "LLOUT"); 155 snd_soc_dapm_nc_pin(codec, "RLOUT"); 156 snd_soc_dapm_nc_pin(codec, "HPRCOM"); 157 158 /* always connected */ 159 snd_soc_dapm_enable_pin(codec, "Audio In"); 160 161 /* must correspond to audio_out_mux.private_value initializer */ 162 snd_soc_dapm_disable_pin(codec, "Audio Out Differential"); 163 snd_soc_dapm_sync(codec); 164 snd_soc_dapm_enable_pin(codec, "Audio Out Stereo"); 165 166 snd_soc_dapm_sync(codec); 167 168 snd_ctl_add(codec->card, snd_ctl_new1(&audio_out_mux, codec)); 169 170 return 0; 171} 172 173/* s6105 digital audio interface glue - connects codec <--> CPU */ 174static struct snd_soc_dai_link s6105_dai = { 175 .name = "TLV320AIC31", 176 .stream_name = "AIC31", 177 .cpu_dai = &s6000_i2s_dai, 178 .codec_dai = &aic3x_dai, 179 .init = s6105_aic3x_init, 180 .ops = &s6105_ops, 181}; 182 183/* s6105 audio machine driver */ 184static struct snd_soc_card snd_soc_card_s6105 = { 185 .name = "Stretch IP Camera", 186 .platform = &s6000_soc_platform, 187 .dai_link = &s6105_dai, 188 .num_links = 1, 189}; 190 191/* s6105 audio private data */ 192static struct aic3x_setup_data s6105_aic3x_setup = { 193}; 194 195/* s6105 audio subsystem */ 196static struct snd_soc_device s6105_snd_devdata = { 197 .card = &snd_soc_card_s6105, 198 .codec_dev = &soc_codec_dev_aic3x, 199 .codec_data = &s6105_aic3x_setup, 200}; 201 202static struct s6000_snd_platform_data __initdata s6105_snd_data = { 203 .wide = 0, 204 .channel_in = 0, 205 .channel_out = 1, 206 .lines_in = 1, 207 .lines_out = 1, 208 .same_rate = 1, 209}; 210 211static struct platform_device *s6105_snd_device; 212 213/* temporary i2c device creation until this can be moved into the machine 214 * support file. 215*/ 216static struct i2c_board_info i2c_device[] = { 217 { I2C_BOARD_INFO("tlv320aic33", 0x18), } 218}; 219 220static int __init s6105_init(void) 221{ 222 int ret; 223 224 i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device)); 225 226 s6105_snd_device = platform_device_alloc("soc-audio", -1); 227 if (!s6105_snd_device) 228 return -ENOMEM; 229 230 platform_set_drvdata(s6105_snd_device, &s6105_snd_devdata); 231 s6105_snd_devdata.dev = &s6105_snd_device->dev; 232 platform_device_add_data(s6105_snd_device, &s6105_snd_data, 233 sizeof(s6105_snd_data)); 234 235 ret = platform_device_add(s6105_snd_device); 236 if (ret) 237 platform_device_put(s6105_snd_device); 238 239 return ret; 240} 241 242static void __exit s6105_exit(void) 243{ 244 platform_device_unregister(s6105_snd_device); 245} 246 247module_init(s6105_init); 248module_exit(s6105_exit); 249 250MODULE_AUTHOR("Daniel Gloeckner"); 251MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver"); 252MODULE_LICENSE("GPL"); 253