1 #include <linux/module.h> 2 #include <linux/slab.h> 3 #include <sound/pcm.h> 4 #include <sound/pcm_params.h> 5 #include <sound/soc.h> 6 #include <sound/initval.h> 7 8 #include <linux/i2c.h> 9 10 #include <linux/mfd/si476x-core.h> 11 12 enum si476x_audio_registers { 13 SI476X_DIGITAL_IO_OUTPUT_FORMAT = 0x0203, 14 SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202, 15 }; 16 17 enum si476x_digital_io_output_format { 18 SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT = 11, 19 SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8, 20 }; 21 22 #define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \ 23 (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)) 24 #define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0b1111110) 25 26 enum si476x_daudio_formats { 27 SI476X_DAUDIO_MODE_I2S = (0x0 << 1), 28 SI476X_DAUDIO_MODE_DSP_A = (0x6 << 1), 29 SI476X_DAUDIO_MODE_DSP_B = (0x7 << 1), 30 SI476X_DAUDIO_MODE_LEFT_J = (0x8 << 1), 31 SI476X_DAUDIO_MODE_RIGHT_J = (0x9 << 1), 32 33 SI476X_DAUDIO_MODE_IB = (1 << 5), 34 SI476X_DAUDIO_MODE_IF = (1 << 6), 35 }; 36 37 enum si476x_pcm_format { 38 SI476X_PCM_FORMAT_S8 = 2, 39 SI476X_PCM_FORMAT_S16_LE = 4, 40 SI476X_PCM_FORMAT_S20_3LE = 5, 41 SI476X_PCM_FORMAT_S24_LE = 6, 42 }; 43 44 static unsigned int si476x_codec_read(struct snd_soc_codec *codec, 45 unsigned int reg) 46 { 47 int err; 48 struct si476x_core *core = codec->control_data; 49 50 si476x_core_lock(core); 51 err = si476x_core_cmd_get_property(core, reg); 52 si476x_core_unlock(core); 53 54 return err; 55 } 56 57 static int si476x_codec_write(struct snd_soc_codec *codec, 58 unsigned int reg, unsigned int val) 59 { 60 int err; 61 struct si476x_core *core = codec->control_data; 62 63 si476x_core_lock(core); 64 err = si476x_core_cmd_set_property(core, reg, val); 65 si476x_core_unlock(core); 66 67 return err; 68 } 69 70 static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, 71 unsigned int fmt) 72 { 73 int err; 74 u16 format = 0; 75 76 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) 77 return -EINVAL; 78 79 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 80 case SND_SOC_DAIFMT_DSP_A: 81 format |= SI476X_DAUDIO_MODE_DSP_A; 82 break; 83 case SND_SOC_DAIFMT_DSP_B: 84 format |= SI476X_DAUDIO_MODE_DSP_B; 85 break; 86 case SND_SOC_DAIFMT_I2S: 87 format |= SI476X_DAUDIO_MODE_I2S; 88 break; 89 case SND_SOC_DAIFMT_RIGHT_J: 90 format |= SI476X_DAUDIO_MODE_RIGHT_J; 91 break; 92 case SND_SOC_DAIFMT_LEFT_J: 93 format |= SI476X_DAUDIO_MODE_LEFT_J; 94 break; 95 default: 96 return -EINVAL; 97 } 98 99 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 100 case SND_SOC_DAIFMT_DSP_A: 101 case SND_SOC_DAIFMT_DSP_B: 102 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 103 case SND_SOC_DAIFMT_NB_NF: 104 break; 105 case SND_SOC_DAIFMT_IB_NF: 106 format |= SI476X_DAUDIO_MODE_IB; 107 break; 108 default: 109 return -EINVAL; 110 } 111 break; 112 case SND_SOC_DAIFMT_I2S: 113 case SND_SOC_DAIFMT_RIGHT_J: 114 case SND_SOC_DAIFMT_LEFT_J: 115 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 116 case SND_SOC_DAIFMT_NB_NF: 117 break; 118 case SND_SOC_DAIFMT_IB_IF: 119 format |= SI476X_DAUDIO_MODE_IB | 120 SI476X_DAUDIO_MODE_IF; 121 break; 122 case SND_SOC_DAIFMT_IB_NF: 123 format |= SI476X_DAUDIO_MODE_IB; 124 break; 125 case SND_SOC_DAIFMT_NB_IF: 126 format |= SI476X_DAUDIO_MODE_IF; 127 break; 128 default: 129 return -EINVAL; 130 } 131 break; 132 default: 133 return -EINVAL; 134 } 135 136 err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT, 137 SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK, 138 format); 139 if (err < 0) { 140 dev_err(codec_dai->codec->dev, "Failed to set output format\n"); 141 return err; 142 } 143 144 return 0; 145 } 146 147 static int si476x_codec_hw_params(struct snd_pcm_substream *substream, 148 struct snd_pcm_hw_params *params, 149 struct snd_soc_dai *dai) 150 { 151 int rate, width, err; 152 153 rate = params_rate(params); 154 if (rate < 32000 || rate > 48000) { 155 dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate); 156 return -EINVAL; 157 } 158 159 switch (params_format(params)) { 160 case SNDRV_PCM_FORMAT_S8: 161 width = SI476X_PCM_FORMAT_S8; 162 case SNDRV_PCM_FORMAT_S16_LE: 163 width = SI476X_PCM_FORMAT_S16_LE; 164 break; 165 case SNDRV_PCM_FORMAT_S20_3LE: 166 width = SI476X_PCM_FORMAT_S20_3LE; 167 break; 168 case SNDRV_PCM_FORMAT_S24_LE: 169 width = SI476X_PCM_FORMAT_S24_LE; 170 break; 171 default: 172 return -EINVAL; 173 } 174 175 err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE, 176 rate); 177 if (err < 0) { 178 dev_err(dai->codec->dev, "Failed to set sample rate\n"); 179 return err; 180 } 181 182 err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT, 183 SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK, 184 (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | 185 (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)); 186 if (err < 0) { 187 dev_err(dai->codec->dev, "Failed to set output width\n"); 188 return err; 189 } 190 191 return 0; 192 } 193 194 static int si476x_codec_probe(struct snd_soc_codec *codec) 195 { 196 codec->control_data = i2c_mfd_cell_to_core(codec->dev); 197 return 0; 198 } 199 200 static struct snd_soc_dai_ops si476x_dai_ops = { 201 .hw_params = si476x_codec_hw_params, 202 .set_fmt = si476x_codec_set_dai_fmt, 203 }; 204 205 static struct snd_soc_dai_driver si476x_dai = { 206 .name = "si476x-codec", 207 .capture = { 208 .stream_name = "Capture", 209 .channels_min = 2, 210 .channels_max = 2, 211 212 .rates = SNDRV_PCM_RATE_32000 | 213 SNDRV_PCM_RATE_44100 | 214 SNDRV_PCM_RATE_48000, 215 .formats = SNDRV_PCM_FMTBIT_S8 | 216 SNDRV_PCM_FMTBIT_S16_LE | 217 SNDRV_PCM_FMTBIT_S20_3LE | 218 SNDRV_PCM_FMTBIT_S24_LE 219 }, 220 .ops = &si476x_dai_ops, 221 }; 222 223 static struct snd_soc_codec_driver soc_codec_dev_si476x = { 224 .probe = si476x_codec_probe, 225 .read = si476x_codec_read, 226 .write = si476x_codec_write, 227 }; 228 229 static int si476x_platform_probe(struct platform_device *pdev) 230 { 231 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x, 232 &si476x_dai, 1); 233 } 234 235 static int si476x_platform_remove(struct platform_device *pdev) 236 { 237 snd_soc_unregister_codec(&pdev->dev); 238 return 0; 239 } 240 241 MODULE_ALIAS("platform:si476x-codec"); 242 243 static struct platform_driver si476x_platform_driver = { 244 .driver = { 245 .name = "si476x-codec", 246 .owner = THIS_MODULE, 247 }, 248 .probe = si476x_platform_probe, 249 .remove = si476x_platform_remove, 250 }; 251 module_platform_driver(si476x_platform_driver); 252 253 MODULE_AUTHOR("Andrey Smirnov <andrey.smirnov@convergeddevices.net>"); 254 MODULE_DESCRIPTION("ASoC Si4761/64 codec driver"); 255 MODULE_LICENSE("GPL"); 256