1/* 2 * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode 3 * 4 * Copyright 2010 Eric B��nard, Eukr��a Electromatique <eric@eukrea.com> 5 * 6 * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c 7 * which is Copyright 2009 Simtec Electronics 8 * and on sound/soc/imx/phycore-ac97.c which is 9 * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the 13 * Free Software Foundation; either version 2 of the License, or (at your 14 * option) any later version. 15 * 16 */ 17 18#include <linux/errno.h> 19#include <linux/module.h> 20#include <linux/moduleparam.h> 21#include <linux/of.h> 22#include <linux/of_platform.h> 23#include <linux/device.h> 24#include <linux/i2c.h> 25#include <sound/core.h> 26#include <sound/pcm.h> 27#include <sound/soc.h> 28#include <asm/mach-types.h> 29 30#include "../codecs/tlv320aic23.h" 31#include "imx-ssi.h" 32#include "fsl_ssi.h" 33#include "imx-audmux.h" 34 35#define CODEC_CLOCK 12000000 36 37static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, 38 struct snd_pcm_hw_params *params) 39{ 40 struct snd_soc_pcm_runtime *rtd = substream->private_data; 41 struct snd_soc_dai *codec_dai = rtd->codec_dai; 42 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 43 int ret; 44 45 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 46 CODEC_CLOCK, SND_SOC_CLOCK_OUT); 47 if (ret) { 48 dev_err(cpu_dai->dev, 49 "Failed to set the codec sysclk.\n"); 50 return ret; 51 } 52 53 snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0); 54 55 ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, 56 SND_SOC_CLOCK_IN); 57 /* fsl_ssi lacks the set_sysclk ops */ 58 if (ret && ret != -EINVAL) { 59 dev_err(cpu_dai->dev, 60 "Can't set the IMX_SSP_SYS_CLK CPU system clock.\n"); 61 return ret; 62 } 63 64 return 0; 65} 66 67static struct snd_soc_ops eukrea_tlv320_snd_ops = { 68 .hw_params = eukrea_tlv320_hw_params, 69}; 70 71static struct snd_soc_dai_link eukrea_tlv320_dai = { 72 .name = "tlv320aic23", 73 .stream_name = "TLV320AIC23", 74 .codec_dai_name = "tlv320aic23-hifi", 75 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 76 SND_SOC_DAIFMT_CBM_CFM, 77 .ops = &eukrea_tlv320_snd_ops, 78}; 79 80static struct snd_soc_card eukrea_tlv320 = { 81 .owner = THIS_MODULE, 82 .dai_link = &eukrea_tlv320_dai, 83 .num_links = 1, 84}; 85 86static int eukrea_tlv320_probe(struct platform_device *pdev) 87{ 88 int ret; 89 int int_port = 0, ext_port; 90 struct device_node *np = pdev->dev.of_node; 91 struct device_node *ssi_np = NULL, *codec_np = NULL; 92 93 eukrea_tlv320.dev = &pdev->dev; 94 if (np) { 95 ret = snd_soc_of_parse_card_name(&eukrea_tlv320, 96 "eukrea,model"); 97 if (ret) { 98 dev_err(&pdev->dev, 99 "eukrea,model node missing or invalid.\n"); 100 goto err; 101 } 102 103 ssi_np = of_parse_phandle(pdev->dev.of_node, 104 "ssi-controller", 0); 105 if (!ssi_np) { 106 dev_err(&pdev->dev, 107 "ssi-controller missing or invalid.\n"); 108 ret = -ENODEV; 109 goto err; 110 } 111 112 codec_np = of_parse_phandle(ssi_np, "codec-handle", 0); 113 if (codec_np) 114 eukrea_tlv320_dai.codec_of_node = codec_np; 115 else 116 dev_err(&pdev->dev, "codec-handle node missing or invalid.\n"); 117 118 ret = of_property_read_u32(np, "fsl,mux-int-port", &int_port); 119 if (ret) { 120 dev_err(&pdev->dev, 121 "fsl,mux-int-port node missing or invalid.\n"); 122 return ret; 123 } 124 ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port); 125 if (ret) { 126 dev_err(&pdev->dev, 127 "fsl,mux-ext-port node missing or invalid.\n"); 128 return ret; 129 } 130 131 /* 132 * The port numbering in the hardware manual starts at 1, while 133 * the audmux API expects it starts at 0. 134 */ 135 int_port--; 136 ext_port--; 137 138 eukrea_tlv320_dai.cpu_of_node = ssi_np; 139 eukrea_tlv320_dai.platform_of_node = ssi_np; 140 } else { 141 eukrea_tlv320_dai.cpu_dai_name = "imx-ssi.0"; 142 eukrea_tlv320_dai.platform_name = "imx-ssi.0"; 143 eukrea_tlv320_dai.codec_name = "tlv320aic23-codec.0-001a"; 144 eukrea_tlv320.name = "cpuimx-audio"; 145 } 146 147 if (machine_is_eukrea_cpuimx27() || 148 of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux")) { 149 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, 150 IMX_AUDMUX_V1_PCR_SYN | 151 IMX_AUDMUX_V1_PCR_TFSDIR | 152 IMX_AUDMUX_V1_PCR_TCLKDIR | 153 IMX_AUDMUX_V1_PCR_RFSDIR | 154 IMX_AUDMUX_V1_PCR_RCLKDIR | 155 IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | 156 IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | 157 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) 158 ); 159 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4, 160 IMX_AUDMUX_V1_PCR_SYN | 161 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) 162 ); 163 } else if (machine_is_eukrea_cpuimx25sd() || 164 machine_is_eukrea_cpuimx35sd() || 165 machine_is_eukrea_cpuimx51sd() || 166 of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux")) { 167 if (!np) 168 ext_port = machine_is_eukrea_cpuimx25sd() ? 169 4 : 3; 170 171 imx_audmux_v2_configure_port(int_port, 172 IMX_AUDMUX_V2_PTCR_SYN | 173 IMX_AUDMUX_V2_PTCR_TFSDIR | 174 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 175 IMX_AUDMUX_V2_PTCR_TCLKDIR | 176 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port), 177 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port) 178 ); 179 imx_audmux_v2_configure_port(ext_port, 180 IMX_AUDMUX_V2_PTCR_SYN, 181 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port) 182 ); 183 } else { 184 if (np) { 185 /* The eukrea,asoc-tlv320 driver was explicitly 186 * requested (through the device tree). 187 */ 188 dev_err(&pdev->dev, 189 "Missing or invalid audmux DT node.\n"); 190 return -ENODEV; 191 } else { 192 /* Return happy. 193 * We might run on a totally different machine. 194 */ 195 return 0; 196 } 197 } 198 199 ret = snd_soc_register_card(&eukrea_tlv320); 200err: 201 if (ret) 202 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); 203 of_node_put(ssi_np); 204 205 return ret; 206} 207 208static int eukrea_tlv320_remove(struct platform_device *pdev) 209{ 210 snd_soc_unregister_card(&eukrea_tlv320); 211 212 return 0; 213} 214 215static const struct of_device_id imx_tlv320_dt_ids[] = { 216 { .compatible = "eukrea,asoc-tlv320"}, 217 { /* sentinel */ } 218}; 219MODULE_DEVICE_TABLE(of, imx_tlv320_dt_ids); 220 221static struct platform_driver eukrea_tlv320_driver = { 222 .driver = { 223 .name = "eukrea_tlv320", 224 .of_match_table = imx_tlv320_dt_ids, 225 }, 226 .probe = eukrea_tlv320_probe, 227 .remove = eukrea_tlv320_remove, 228}; 229 230module_platform_driver(eukrea_tlv320_driver); 231 232MODULE_AUTHOR("Eric B��nard <eric@eukrea.com>"); 233MODULE_DESCRIPTION("CPUIMX ALSA SoC driver"); 234MODULE_LICENSE("GPL"); 235MODULE_ALIAS("platform:eukrea_tlv320"); 236