Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.11 339 lines 8.1 kB view raw
1/* 2 * raumfeld_audio.c -- SoC audio for Raumfeld audio devices 3 * 4 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> 5 * 6 * based on code from: 7 * 8 * Wolfson Microelectronics PLC. 9 * Openedhand Ltd. 10 * Liam Girdwood <lrg@slimlogic.co.uk> 11 * Richard Purdie <richard@openedhand.com> 12 * 13 * This program is free software; you can redistribute it and/or modify it 14 * under the terms of the GNU General Public License as published by the 15 * Free Software Foundation; either version 2 of the License, or (at your 16 * option) any later version. 17 */ 18 19#include <linux/module.h> 20#include <linux/i2c.h> 21#include <linux/delay.h> 22#include <linux/gpio.h> 23#include <sound/pcm.h> 24#include <sound/soc.h> 25 26#include <asm/mach-types.h> 27 28#include "pxa-ssp.h" 29 30#define GPIO_SPDIF_RESET (38) 31#define GPIO_MCLK_RESET (111) 32#define GPIO_CODEC_RESET (120) 33 34static struct i2c_client *max9486_client; 35static struct i2c_board_info max9486_hwmon_info = { 36 I2C_BOARD_INFO("max9485", 0x63), 37}; 38 39#define MAX9485_MCLK_FREQ_112896 0x22 40#define MAX9485_MCLK_FREQ_122880 0x23 41#define MAX9485_MCLK_FREQ_225792 0x32 42#define MAX9485_MCLK_FREQ_245760 0x33 43 44static void set_max9485_clk(char clk) 45{ 46 i2c_master_send(max9486_client, &clk, 1); 47} 48 49static void raumfeld_enable_audio(bool en) 50{ 51 if (en) { 52 gpio_set_value(GPIO_MCLK_RESET, 1); 53 54 /* wait some time to let the clocks become stable */ 55 msleep(100); 56 57 gpio_set_value(GPIO_SPDIF_RESET, 1); 58 gpio_set_value(GPIO_CODEC_RESET, 1); 59 } else { 60 gpio_set_value(GPIO_MCLK_RESET, 0); 61 gpio_set_value(GPIO_SPDIF_RESET, 0); 62 gpio_set_value(GPIO_CODEC_RESET, 0); 63 } 64} 65 66/* CS4270 */ 67static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) 68{ 69 struct snd_soc_pcm_runtime *rtd = substream->private_data; 70 struct snd_soc_dai *codec_dai = rtd->codec_dai; 71 72 /* set freq to 0 to enable all possible codec sample rates */ 73 return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); 74} 75 76static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) 77{ 78 struct snd_soc_pcm_runtime *rtd = substream->private_data; 79 struct snd_soc_dai *codec_dai = rtd->codec_dai; 80 81 /* set freq to 0 to enable all possible codec sample rates */ 82 snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); 83} 84 85static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, 86 struct snd_pcm_hw_params *params) 87{ 88 struct snd_soc_pcm_runtime *rtd = substream->private_data; 89 struct snd_soc_dai *codec_dai = rtd->codec_dai; 90 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 91 unsigned int fmt, clk = 0; 92 int ret = 0; 93 94 switch (params_rate(params)) { 95 case 44100: 96 set_max9485_clk(MAX9485_MCLK_FREQ_112896); 97 clk = 11289600; 98 break; 99 case 48000: 100 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 101 clk = 12288000; 102 break; 103 case 88200: 104 set_max9485_clk(MAX9485_MCLK_FREQ_225792); 105 clk = 22579200; 106 break; 107 case 96000: 108 set_max9485_clk(MAX9485_MCLK_FREQ_245760); 109 clk = 24576000; 110 break; 111 default: 112 return -EINVAL; 113 } 114 115 fmt = SND_SOC_DAIFMT_I2S | 116 SND_SOC_DAIFMT_NB_NF | 117 SND_SOC_DAIFMT_CBS_CFS; 118 119 /* setup the CODEC DAI */ 120 ret = snd_soc_dai_set_fmt(codec_dai, fmt); 121 if (ret < 0) 122 return ret; 123 124 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); 125 if (ret < 0) 126 return ret; 127 128 /* setup the CPU DAI */ 129 ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); 130 if (ret < 0) 131 return ret; 132 133 ret = snd_soc_dai_set_fmt(cpu_dai, fmt); 134 if (ret < 0) 135 return ret; 136 137 ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); 138 if (ret < 0) 139 return ret; 140 141 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); 142 if (ret < 0) 143 return ret; 144 145 return 0; 146} 147 148static struct snd_soc_ops raumfeld_cs4270_ops = { 149 .startup = raumfeld_cs4270_startup, 150 .shutdown = raumfeld_cs4270_shutdown, 151 .hw_params = raumfeld_cs4270_hw_params, 152}; 153 154static int raumfeld_analog_suspend(struct snd_soc_card *card) 155{ 156 raumfeld_enable_audio(false); 157 return 0; 158} 159 160static int raumfeld_analog_resume(struct snd_soc_card *card) 161{ 162 raumfeld_enable_audio(true); 163 return 0; 164} 165 166/* AK4104 */ 167 168static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, 169 struct snd_pcm_hw_params *params) 170{ 171 struct snd_soc_pcm_runtime *rtd = substream->private_data; 172 struct snd_soc_dai *codec_dai = rtd->codec_dai; 173 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 174 int fmt, ret = 0, clk = 0; 175 176 switch (params_rate(params)) { 177 case 44100: 178 set_max9485_clk(MAX9485_MCLK_FREQ_112896); 179 clk = 11289600; 180 break; 181 case 48000: 182 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 183 clk = 12288000; 184 break; 185 case 88200: 186 set_max9485_clk(MAX9485_MCLK_FREQ_225792); 187 clk = 22579200; 188 break; 189 case 96000: 190 set_max9485_clk(MAX9485_MCLK_FREQ_245760); 191 clk = 24576000; 192 break; 193 default: 194 return -EINVAL; 195 } 196 197 fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; 198 199 /* setup the CODEC DAI */ 200 ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); 201 if (ret < 0) 202 return ret; 203 204 /* setup the CPU DAI */ 205 ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); 206 if (ret < 0) 207 return ret; 208 209 ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); 210 if (ret < 0) 211 return ret; 212 213 ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); 214 if (ret < 0) 215 return ret; 216 217 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); 218 if (ret < 0) 219 return ret; 220 221 return 0; 222} 223 224static struct snd_soc_ops raumfeld_ak4104_ops = { 225 .hw_params = raumfeld_ak4104_hw_params, 226}; 227 228#define DAI_LINK_CS4270 \ 229{ \ 230 .name = "CS4270", \ 231 .stream_name = "CS4270", \ 232 .cpu_dai_name = "pxa-ssp-dai.0", \ 233 .platform_name = "pxa-pcm-audio", \ 234 .codec_dai_name = "cs4270-hifi", \ 235 .codec_name = "cs4270.0-0048", \ 236 .ops = &raumfeld_cs4270_ops, \ 237} 238 239#define DAI_LINK_AK4104 \ 240{ \ 241 .name = "ak4104", \ 242 .stream_name = "Playback", \ 243 .cpu_dai_name = "pxa-ssp-dai.1", \ 244 .codec_dai_name = "ak4104-hifi", \ 245 .platform_name = "pxa-pcm-audio", \ 246 .ops = &raumfeld_ak4104_ops, \ 247 .codec_name = "spi0.0", \ 248} 249 250static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = 251{ 252 DAI_LINK_CS4270, 253 DAI_LINK_AK4104, 254}; 255 256static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = 257{ 258 DAI_LINK_CS4270, 259}; 260 261static struct snd_soc_card snd_soc_raumfeld_connector = { 262 .name = "Raumfeld Connector", 263 .owner = THIS_MODULE, 264 .dai_link = snd_soc_raumfeld_connector_dai, 265 .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai), 266 .suspend_post = raumfeld_analog_suspend, 267 .resume_pre = raumfeld_analog_resume, 268}; 269 270static struct snd_soc_card snd_soc_raumfeld_speaker = { 271 .name = "Raumfeld Speaker", 272 .owner = THIS_MODULE, 273 .dai_link = snd_soc_raumfeld_speaker_dai, 274 .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai), 275 .suspend_post = raumfeld_analog_suspend, 276 .resume_pre = raumfeld_analog_resume, 277}; 278 279static struct platform_device *raumfeld_audio_device; 280 281static int __init raumfeld_audio_init(void) 282{ 283 int ret; 284 285 if (!machine_is_raumfeld_speaker() && 286 !machine_is_raumfeld_connector()) 287 return 0; 288 289 max9486_client = i2c_new_device(i2c_get_adapter(0), 290 &max9486_hwmon_info); 291 292 if (!max9486_client) 293 return -ENOMEM; 294 295 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 296 297 /* Register analog device */ 298 raumfeld_audio_device = platform_device_alloc("soc-audio", 0); 299 if (!raumfeld_audio_device) 300 return -ENOMEM; 301 302 if (machine_is_raumfeld_speaker()) 303 platform_set_drvdata(raumfeld_audio_device, 304 &snd_soc_raumfeld_speaker); 305 306 if (machine_is_raumfeld_connector()) 307 platform_set_drvdata(raumfeld_audio_device, 308 &snd_soc_raumfeld_connector); 309 310 ret = platform_device_add(raumfeld_audio_device); 311 if (ret < 0) { 312 platform_device_put(raumfeld_audio_device); 313 return ret; 314 } 315 316 raumfeld_enable_audio(true); 317 return 0; 318} 319 320static void __exit raumfeld_audio_exit(void) 321{ 322 raumfeld_enable_audio(false); 323 324 platform_device_unregister(raumfeld_audio_device); 325 326 i2c_unregister_device(max9486_client); 327 328 gpio_free(GPIO_MCLK_RESET); 329 gpio_free(GPIO_CODEC_RESET); 330 gpio_free(GPIO_SPDIF_RESET); 331} 332 333module_init(raumfeld_audio_init); 334module_exit(raumfeld_audio_exit); 335 336/* Module information */ 337MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); 338MODULE_DESCRIPTION("Raumfeld audio SoC"); 339MODULE_LICENSE("GPL");