1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Socionext UniPhier AIO ALSA driver for LD11/LD20. 4 // 5 // Copyright (c) 2016-2018 Socionext Inc. 6 // 7 // This program is free software; you can redistribute it and/or 8 // modify it under the terms of the GNU General Public License 9 // as published by the Free Software Foundation; version 2 10 // of the License. 11 // 12 // This program is distributed in the hope that it will be useful, 13 // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 // GNU General Public License for more details. 16 // 17 // You should have received a copy of the GNU General Public License 18 // along with this program; if not, see <http://www.gnu.org/licenses/>. 19 20 #include <linux/module.h> 21 22 #include "aio.h" 23 24 static const struct uniphier_aio_spec uniphier_aio_ld11[] = { 25 /* for HDMI PCM In, Pin:AI1Dx */ 26 { 27 .name = AUD_NAME_PCMIN1, 28 .gname = AUD_GNAME_HDMI, 29 .swm = { 30 .type = PORT_TYPE_I2S, 31 .dir = PORT_DIR_INPUT, 32 .rb = { 21, 14, }, 33 .ch = { 21, 14, }, 34 .iif = { 5, 3, }, 35 .iport = { 0, AUD_HW_PCMIN1, }, 36 }, 37 }, 38 39 /* for SIF In, Pin:AI2Dx */ 40 { 41 .name = AUD_NAME_PCMIN2, 42 .swm = { 43 .type = PORT_TYPE_I2S, 44 .dir = PORT_DIR_INPUT, 45 .rb = { 22, 15, }, 46 .ch = { 22, 15, }, 47 .iif = { 6, 4, }, 48 .iport = { 1, AUD_HW_PCMIN2, }, 49 }, 50 }, 51 52 /* for Line In, Pin:AI3Dx */ 53 { 54 .name = AUD_NAME_PCMIN3, 55 .gname = AUD_GNAME_LINE, 56 .swm = { 57 .type = PORT_TYPE_EVE, 58 .dir = PORT_DIR_INPUT, 59 .rb = { 23, 16, }, 60 .ch = { 23, 16, }, 61 .iif = { 7, 5, }, 62 .iport = { 2, AUD_HW_PCMIN3, }, 63 }, 64 }, 65 66 /* for S/PDIF In, Pin:AI1IEC */ 67 { 68 .name = AUD_NAME_IECIN1, 69 .gname = AUD_GNAME_IEC, 70 .swm = { 71 .type = PORT_TYPE_SPDIF, 72 .dir = PORT_DIR_INPUT, 73 .rb = { 26, 17, }, 74 .ch = { 26, 17, }, 75 .iif = { 10, 6, }, 76 .iport = { 3, AUD_HW_IECIN1, }, 77 }, 78 }, 79 80 /* for Speaker, Pin:AO1Dx */ 81 { 82 .name = AUD_NAME_HPCMOUT1, 83 .swm = { 84 .type = PORT_TYPE_I2S, 85 .dir = PORT_DIR_OUTPUT, 86 .rb = { 0, 0, }, 87 .ch = { 0, 0, }, 88 .oif = { 0, 0, }, 89 .oport = { 0, AUD_HW_HPCMOUT1, }, 90 }, 91 }, 92 93 /* for HDMI PCM, Pin:AO2Dx */ 94 { 95 .name = AUD_NAME_PCMOUT1, 96 .gname = AUD_GNAME_HDMI, 97 .swm = { 98 .type = PORT_TYPE_I2S, 99 .dir = PORT_DIR_OUTPUT, 100 .rb = { 0, 0, }, 101 .ch = { 0, 0, }, 102 .oif = { 0, 0, }, 103 .oport = { 3, AUD_HW_PCMOUT1, }, 104 }, 105 }, 106 107 /* for Line Out, Pin:LO2_x */ 108 { 109 .name = AUD_NAME_PCMOUT2, 110 .gname = AUD_GNAME_LINE, 111 .swm = { 112 .type = PORT_TYPE_EVE, 113 .dir = PORT_DIR_OUTPUT, 114 .rb = { 2, 2, }, 115 .ch = { 2, 2, }, 116 .oif = { 2, 2, }, 117 .oport = { 1, AUD_HW_PCMOUT2, }, 118 }, 119 }, 120 121 /* for Headphone, Pin:HP1_x */ 122 { 123 .name = AUD_NAME_PCMOUT3, 124 .swm = { 125 .type = PORT_TYPE_EVE, 126 .dir = PORT_DIR_OUTPUT, 127 .rb = { 3, 3, }, 128 .ch = { 3, 3, }, 129 .oif = { 3, 3, }, 130 .oport = { 2, AUD_HW_PCMOUT3, }, 131 }, 132 }, 133 134 /* for HW Sampling Rate Converter */ 135 { 136 .name = AUD_NAME_EPCMOUT2, 137 .swm = { 138 .type = PORT_TYPE_CONV, 139 .dir = PORT_DIR_OUTPUT, 140 .rb = { 7, 5, }, 141 .ch = { 7, 5, }, 142 .oif = { 7, 5, }, 143 .oport = { 6, AUD_HW_EPCMOUT2, }, 144 .och = { 17, 12, }, 145 .iif = { 1, 1, }, 146 }, 147 }, 148 149 /* for HW Sampling Rate Converter 2 */ 150 { 151 .name = AUD_NAME_EPCMOUT3, 152 .swm = { 153 .type = PORT_TYPE_CONV, 154 .dir = PORT_DIR_OUTPUT, 155 .rb = { 8, 6, }, 156 .ch = { 8, 6, }, 157 .oif = { 8, 6, }, 158 .oport = { 7, AUD_HW_EPCMOUT3, }, 159 .och = { 18, 13, }, 160 .iif = { 2, 2, }, 161 }, 162 }, 163 164 /* for S/PDIF Out, Pin:AO1IEC */ 165 { 166 .name = AUD_NAME_HIECOUT1, 167 .gname = AUD_GNAME_IEC, 168 .swm = { 169 .type = PORT_TYPE_SPDIF, 170 .dir = PORT_DIR_OUTPUT, 171 .rb = { 1, 1, }, 172 .ch = { 1, 1, }, 173 .oif = { 1, 1, }, 174 .oport = { 12, AUD_HW_HIECOUT1, }, 175 }, 176 }, 177 178 /* for S/PDIF Out, Pin:AO1IEC, Compress */ 179 { 180 .name = AUD_NAME_HIECCOMPOUT1, 181 .gname = AUD_GNAME_IEC, 182 .swm = { 183 .type = PORT_TYPE_SPDIF, 184 .dir = PORT_DIR_OUTPUT, 185 .rb = { 1, 1, }, 186 .ch = { 1, 1, }, 187 .oif = { 1, 1, }, 188 .oport = { 12, AUD_HW_HIECOUT1, }, 189 }, 190 }, 191 }; 192 193 static const struct uniphier_aio_pll uniphier_aio_pll_ld11[] = { 194 [AUD_PLL_A1] = { .enable = true, }, 195 [AUD_PLL_F1] = { .enable = true, }, 196 [AUD_PLL_A2] = { .enable = true, }, 197 [AUD_PLL_F2] = { .enable = true, }, 198 [AUD_PLL_APLL] = { .enable = true, }, 199 [AUD_PLL_RX0] = { .enable = true, }, 200 [AUD_PLL_USB0] = { .enable = true, }, 201 [AUD_PLL_HSC0] = { .enable = true, }, 202 }; 203 204 static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai) 205 { 206 int ret; 207 208 ret = uniphier_aio_dai_probe(dai); 209 if (ret < 0) 210 return ret; 211 212 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); 213 if (ret < 0) 214 return ret; 215 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); 216 if (ret < 0) 217 return ret; 218 219 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); 220 if (ret < 0) 221 return ret; 222 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); 223 if (ret < 0) 224 return ret; 225 226 return 0; 227 } 228 229 static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { 230 { 231 .name = AUD_GNAME_HDMI, 232 .probe = uniphier_aio_ld11_probe, 233 .remove = uniphier_aio_dai_remove, 234 .suspend = uniphier_aio_dai_suspend, 235 .resume = uniphier_aio_dai_resume, 236 .playback = { 237 .stream_name = AUD_NAME_PCMOUT1, 238 .formats = SNDRV_PCM_FMTBIT_S32_LE, 239 .rates = SNDRV_PCM_RATE_48000, 240 .channels_min = 2, 241 .channels_max = 2, 242 }, 243 .capture = { 244 .stream_name = AUD_NAME_PCMIN1, 245 .formats = SNDRV_PCM_FMTBIT_S32_LE, 246 .rates = SNDRV_PCM_RATE_48000 | 247 SNDRV_PCM_RATE_44100 | 248 SNDRV_PCM_RATE_32000, 249 .channels_min = 2, 250 .channels_max = 2, 251 }, 252 .ops = &uniphier_aio_i2s_ops, 253 }, 254 { 255 .name = AUD_NAME_PCMIN2, 256 .probe = uniphier_aio_ld11_probe, 257 .remove = uniphier_aio_dai_remove, 258 .suspend = uniphier_aio_dai_suspend, 259 .resume = uniphier_aio_dai_resume, 260 .capture = { 261 .stream_name = AUD_NAME_PCMIN2, 262 .formats = SNDRV_PCM_FMTBIT_S32_LE, 263 .rates = SNDRV_PCM_RATE_48000, 264 .channels_min = 2, 265 .channels_max = 2, 266 }, 267 .ops = &uniphier_aio_i2s_ops, 268 }, 269 { 270 .name = AUD_GNAME_LINE, 271 .probe = uniphier_aio_ld11_probe, 272 .remove = uniphier_aio_dai_remove, 273 .suspend = uniphier_aio_dai_suspend, 274 .resume = uniphier_aio_dai_resume, 275 .playback = { 276 .stream_name = AUD_NAME_PCMOUT2, 277 .formats = SNDRV_PCM_FMTBIT_S32_LE, 278 .rates = SNDRV_PCM_RATE_48000, 279 .channels_min = 2, 280 .channels_max = 2, 281 }, 282 .capture = { 283 .stream_name = AUD_NAME_PCMIN3, 284 .formats = SNDRV_PCM_FMTBIT_S32_LE, 285 .rates = SNDRV_PCM_RATE_48000, 286 .channels_min = 2, 287 .channels_max = 2, 288 }, 289 .ops = &uniphier_aio_i2s_ops, 290 }, 291 { 292 .name = AUD_NAME_HPCMOUT1, 293 .probe = uniphier_aio_ld11_probe, 294 .remove = uniphier_aio_dai_remove, 295 .suspend = uniphier_aio_dai_suspend, 296 .resume = uniphier_aio_dai_resume, 297 .playback = { 298 .stream_name = AUD_NAME_HPCMOUT1, 299 .formats = SNDRV_PCM_FMTBIT_S32_LE, 300 .rates = SNDRV_PCM_RATE_48000, 301 .channels_min = 2, 302 .channels_max = 2, 303 }, 304 .ops = &uniphier_aio_i2s_ops, 305 }, 306 { 307 .name = AUD_NAME_PCMOUT3, 308 .probe = uniphier_aio_ld11_probe, 309 .remove = uniphier_aio_dai_remove, 310 .suspend = uniphier_aio_dai_suspend, 311 .resume = uniphier_aio_dai_resume, 312 .playback = { 313 .stream_name = AUD_NAME_PCMOUT3, 314 .formats = SNDRV_PCM_FMTBIT_S32_LE, 315 .rates = SNDRV_PCM_RATE_48000, 316 .channels_min = 2, 317 .channels_max = 2, 318 }, 319 .ops = &uniphier_aio_i2s_ops, 320 }, 321 { 322 .name = AUD_NAME_HIECOUT1, 323 .probe = uniphier_aio_ld11_probe, 324 .remove = uniphier_aio_dai_remove, 325 .suspend = uniphier_aio_dai_suspend, 326 .resume = uniphier_aio_dai_resume, 327 .playback = { 328 .stream_name = AUD_NAME_HIECOUT1, 329 .formats = SNDRV_PCM_FMTBIT_S32_LE, 330 .rates = SNDRV_PCM_RATE_48000, 331 .channels_min = 2, 332 .channels_max = 2, 333 }, 334 .ops = &uniphier_aio_spdif_ops, 335 }, 336 { 337 .name = AUD_NAME_EPCMOUT2, 338 .probe = uniphier_aio_ld11_probe, 339 .remove = uniphier_aio_dai_remove, 340 .suspend = uniphier_aio_dai_suspend, 341 .resume = uniphier_aio_dai_resume, 342 .playback = { 343 .stream_name = AUD_NAME_EPCMOUT2, 344 .formats = SNDRV_PCM_FMTBIT_S32_LE, 345 .rates = SNDRV_PCM_RATE_48000 | 346 SNDRV_PCM_RATE_44100 | 347 SNDRV_PCM_RATE_32000, 348 .channels_min = 2, 349 .channels_max = 2, 350 }, 351 .ops = &uniphier_aio_i2s_ops, 352 }, 353 { 354 .name = AUD_NAME_EPCMOUT3, 355 .probe = uniphier_aio_ld11_probe, 356 .remove = uniphier_aio_dai_remove, 357 .suspend = uniphier_aio_dai_suspend, 358 .resume = uniphier_aio_dai_resume, 359 .playback = { 360 .stream_name = AUD_NAME_EPCMOUT3, 361 .formats = SNDRV_PCM_FMTBIT_S32_LE, 362 .rates = SNDRV_PCM_RATE_48000 | 363 SNDRV_PCM_RATE_44100 | 364 SNDRV_PCM_RATE_32000, 365 .channels_min = 2, 366 .channels_max = 2, 367 }, 368 .ops = &uniphier_aio_i2s_ops, 369 }, 370 { 371 .name = AUD_NAME_HIECCOMPOUT1, 372 .probe = uniphier_aio_ld11_probe, 373 .remove = uniphier_aio_dai_remove, 374 .suspend = uniphier_aio_dai_suspend, 375 .resume = uniphier_aio_dai_resume, 376 .compress_new = snd_soc_new_compress, 377 .playback = { 378 .stream_name = AUD_NAME_HIECCOMPOUT1, 379 .channels_min = 1, 380 .channels_max = 1, 381 }, 382 .ops = &uniphier_aio_spdif_ops, 383 }, 384 }; 385 386 static const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec = { 387 .specs = uniphier_aio_ld11, 388 .num_specs = ARRAY_SIZE(uniphier_aio_ld11), 389 .dais = uniphier_aio_dai_ld11, 390 .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11), 391 .plls = uniphier_aio_pll_ld11, 392 .num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11), 393 .addr_ext = 0, 394 }; 395 396 static const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec = { 397 .specs = uniphier_aio_ld11, 398 .num_specs = ARRAY_SIZE(uniphier_aio_ld11), 399 .dais = uniphier_aio_dai_ld11, 400 .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11), 401 .plls = uniphier_aio_pll_ld11, 402 .num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11), 403 .addr_ext = 1, 404 }; 405 406 static const struct of_device_id uniphier_aio_of_match[] = { 407 { 408 .compatible = "socionext,uniphier-ld11-aio", 409 .data = &uniphier_aio_ld11_spec, 410 }, 411 { 412 .compatible = "socionext,uniphier-ld20-aio", 413 .data = &uniphier_aio_ld20_spec, 414 }, 415 {}, 416 }; 417 MODULE_DEVICE_TABLE(of, uniphier_aio_of_match); 418 419 static struct platform_driver uniphier_aio_driver = { 420 .driver = { 421 .name = "snd-uniphier-aio-ld11", 422 .of_match_table = of_match_ptr(uniphier_aio_of_match), 423 }, 424 .probe = uniphier_aio_probe, 425 .remove = uniphier_aio_remove, 426 }; 427 module_platform_driver(uniphier_aio_driver); 428 429 MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); 430 MODULE_DESCRIPTION("UniPhier LD11/LD20 AIO driver."); 431 MODULE_LICENSE("GPL v2"); 432