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#include <sound/soc-dapm.h> 26 27#include <asm/mach-types.h> 28 29#include "../codecs/cs4270.h" 30#include "../codecs/ak4104.h" 31#include "pxa2xx-pcm.h" 32#include "pxa-ssp.h" 33 34#define GPIO_SPDIF_RESET (38) 35#define GPIO_MCLK_RESET (111) 36#define GPIO_CODEC_RESET (120) 37 38static struct i2c_client *max9486_client; 39static struct i2c_board_info max9486_hwmon_info = { 40 I2C_BOARD_INFO("max9485", 0x63), 41}; 42 43#define MAX9485_MCLK_FREQ_112896 0x22 44#define MAX9485_MCLK_FREQ_122880 0x23 45#define MAX9485_MCLK_FREQ_225792 0x32 46#define MAX9485_MCLK_FREQ_245760 0x33 47 48static void set_max9485_clk(char clk) 49{ 50 i2c_master_send(max9486_client, &clk, 1); 51} 52 53static void raumfeld_enable_audio(bool en) 54{ 55 if (en) { 56 gpio_set_value(GPIO_MCLK_RESET, 1); 57 58 /* wait some time to let the clocks become stable */ 59 msleep(100); 60 61 gpio_set_value(GPIO_SPDIF_RESET, 1); 62 gpio_set_value(GPIO_CODEC_RESET, 1); 63 } else { 64 gpio_set_value(GPIO_MCLK_RESET, 0); 65 gpio_set_value(GPIO_SPDIF_RESET, 0); 66 gpio_set_value(GPIO_CODEC_RESET, 0); 67 } 68} 69 70/* CS4270 */ 71static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) 72{ 73 struct snd_soc_pcm_runtime *rtd = substream->private_data; 74 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 75 76 /* set freq to 0 to enable all possible codec sample rates */ 77 return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); 78} 79 80static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) 81{ 82 struct snd_soc_pcm_runtime *rtd = substream->private_data; 83 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 84 85 /* set freq to 0 to enable all possible codec sample rates */ 86 snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); 87} 88 89static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, 90 struct snd_pcm_hw_params *params) 91{ 92 struct snd_soc_pcm_runtime *rtd = substream->private_data; 93 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 94 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 95 unsigned int fmt, clk = 0; 96 int ret = 0; 97 98 switch (params_rate(params)) { 99 case 44100: 100 set_max9485_clk(MAX9485_MCLK_FREQ_112896); 101 clk = 11289600; 102 break; 103 case 48000: 104 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 105 clk = 12288000; 106 break; 107 case 88200: 108 set_max9485_clk(MAX9485_MCLK_FREQ_225792); 109 clk = 22579200; 110 break; 111 case 96000: 112 set_max9485_clk(MAX9485_MCLK_FREQ_245760); 113 clk = 24576000; 114 break; 115 default: 116 return -EINVAL; 117 } 118 119 fmt = SND_SOC_DAIFMT_I2S | 120 SND_SOC_DAIFMT_NB_NF | 121 SND_SOC_DAIFMT_CBS_CFS; 122 123 /* setup the CODEC DAI */ 124 ret = snd_soc_dai_set_fmt(codec_dai, fmt); 125 if (ret < 0) 126 return ret; 127 128 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); 129 if (ret < 0) 130 return ret; 131 132 /* setup the CPU DAI */ 133 ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); 134 if (ret < 0) 135 return ret; 136 137 ret = snd_soc_dai_set_fmt(cpu_dai, fmt); 138 if (ret < 0) 139 return ret; 140 141 ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); 142 if (ret < 0) 143 return ret; 144 145 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); 146 if (ret < 0) 147 return ret; 148 149 return 0; 150} 151 152static struct snd_soc_ops raumfeld_cs4270_ops = { 153 .startup = raumfeld_cs4270_startup, 154 .shutdown = raumfeld_cs4270_shutdown, 155 .hw_params = raumfeld_cs4270_hw_params, 156}; 157 158static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) 159{ 160 raumfeld_enable_audio(false); 161 return 0; 162} 163 164static int raumfeld_line_resume(struct platform_device *pdev) 165{ 166 raumfeld_enable_audio(true); 167 return 0; 168} 169 170static struct snd_soc_dai_link raumfeld_line_dai = { 171 .name = "CS4270", 172 .stream_name = "CS4270", 173 .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], 174 .codec_dai = &cs4270_dai, 175 .ops = &raumfeld_cs4270_ops, 176}; 177 178static struct snd_soc_card snd_soc_line_raumfeld = { 179 .name = "Raumfeld analog", 180 .platform = &pxa2xx_soc_platform, 181 .dai_link = &raumfeld_line_dai, 182 .suspend_post = raumfeld_line_suspend, 183 .resume_pre = raumfeld_line_resume, 184 .num_links = 1, 185}; 186 187 188/* AK4104 */ 189 190static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, 191 struct snd_pcm_hw_params *params) 192{ 193 struct snd_soc_pcm_runtime *rtd = substream->private_data; 194 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 195 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 196 int fmt, ret = 0, clk = 0; 197 198 switch (params_rate(params)) { 199 case 44100: 200 set_max9485_clk(MAX9485_MCLK_FREQ_112896); 201 clk = 11289600; 202 break; 203 case 48000: 204 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 205 clk = 12288000; 206 break; 207 case 88200: 208 set_max9485_clk(MAX9485_MCLK_FREQ_225792); 209 clk = 22579200; 210 break; 211 case 96000: 212 set_max9485_clk(MAX9485_MCLK_FREQ_245760); 213 clk = 24576000; 214 break; 215 default: 216 return -EINVAL; 217 } 218 219 fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; 220 221 /* setup the CODEC DAI */ 222 ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); 223 if (ret < 0) 224 return ret; 225 226 /* setup the CPU DAI */ 227 ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); 228 if (ret < 0) 229 return ret; 230 231 ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); 232 if (ret < 0) 233 return ret; 234 235 ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); 236 if (ret < 0) 237 return ret; 238 239 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); 240 if (ret < 0) 241 return ret; 242 243 return 0; 244} 245 246static struct snd_soc_ops raumfeld_ak4104_ops = { 247 .hw_params = raumfeld_ak4104_hw_params, 248}; 249 250static struct snd_soc_dai_link raumfeld_spdif_dai = { 251 .name = "ak4104", 252 .stream_name = "Playback", 253 .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2], 254 .codec_dai = &ak4104_dai, 255 .ops = &raumfeld_ak4104_ops, 256}; 257 258static struct snd_soc_card snd_soc_spdif_raumfeld = { 259 .name = "Raumfeld S/PDIF", 260 .platform = &pxa2xx_soc_platform, 261 .dai_link = &raumfeld_spdif_dai, 262 .num_links = 1 263}; 264 265/* raumfeld_audio audio subsystem */ 266static struct snd_soc_device raumfeld_line_devdata = { 267 .card = &snd_soc_line_raumfeld, 268 .codec_dev = &soc_codec_device_cs4270, 269}; 270 271static struct snd_soc_device raumfeld_spdif_devdata = { 272 .card = &snd_soc_spdif_raumfeld, 273 .codec_dev = &soc_codec_device_ak4104, 274}; 275 276static struct platform_device *raumfeld_audio_line_device; 277static struct platform_device *raumfeld_audio_spdif_device; 278 279static int __init raumfeld_audio_init(void) 280{ 281 int ret; 282 283 if (!machine_is_raumfeld_speaker() && 284 !machine_is_raumfeld_connector()) 285 return 0; 286 287 max9486_client = i2c_new_device(i2c_get_adapter(0), 288 &max9486_hwmon_info); 289 290 if (!max9486_client) 291 return -ENOMEM; 292 293 set_max9485_clk(MAX9485_MCLK_FREQ_122880); 294 295 /* LINE */ 296 raumfeld_audio_line_device = platform_device_alloc("soc-audio", 0); 297 if (!raumfeld_audio_line_device) 298 return -ENOMEM; 299 300 platform_set_drvdata(raumfeld_audio_line_device, 301 &raumfeld_line_devdata); 302 raumfeld_line_devdata.dev = &raumfeld_audio_line_device->dev; 303 ret = platform_device_add(raumfeld_audio_line_device); 304 if (ret) 305 platform_device_put(raumfeld_audio_line_device); 306 307 /* no S/PDIF on Speakers */ 308 if (machine_is_raumfeld_speaker()) 309 return ret; 310 311 /* S/PDIF */ 312 raumfeld_audio_spdif_device = platform_device_alloc("soc-audio", 1); 313 if (!raumfeld_audio_spdif_device) { 314 platform_device_put(raumfeld_audio_line_device); 315 return -ENOMEM; 316 } 317 318 platform_set_drvdata(raumfeld_audio_spdif_device, 319 &raumfeld_spdif_devdata); 320 raumfeld_spdif_devdata.dev = &raumfeld_audio_spdif_device->dev; 321 ret = platform_device_add(raumfeld_audio_spdif_device); 322 if (ret) { 323 platform_device_put(raumfeld_audio_line_device); 324 platform_device_put(raumfeld_audio_spdif_device); 325 } 326 327 raumfeld_enable_audio(true); 328 329 return ret; 330} 331 332static void __exit raumfeld_audio_exit(void) 333{ 334 raumfeld_enable_audio(false); 335 336 platform_device_unregister(raumfeld_audio_line_device); 337 338 if (machine_is_raumfeld_connector()) 339 platform_device_unregister(raumfeld_audio_spdif_device); 340 341 i2c_unregister_device(max9486_client); 342 343 gpio_free(GPIO_MCLK_RESET); 344 gpio_free(GPIO_CODEC_RESET); 345 gpio_free(GPIO_SPDIF_RESET); 346} 347 348module_init(raumfeld_audio_init); 349module_exit(raumfeld_audio_exit); 350 351/* Module information */ 352MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); 353MODULE_DESCRIPTION("Raumfeld audio SoC"); 354MODULE_LICENSE("GPL"); 355