1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 // 3 // Copyright (c) 2018 BayLibre, SAS. 4 // Author: Jerome Brunet <jbrunet@baylibre.com> 5 6 /* 7 * This driver implements the frontend playback DAI of AXG and G12A based SoCs 8 */ 9 10 #include <linux/bitfield.h> 11 #include <linux/clk.h> 12 #include <linux/regmap.h> 13 #include <linux/module.h> 14 #include <linux/of_platform.h> 15 #include <sound/pcm_params.h> 16 #include <sound/soc.h> 17 #include <sound/soc-dai.h> 18 19 #include "axg-fifo.h" 20 21 #define CTRL0_FRDDR_PP_MODE BIT(30) 22 #define CTRL0_SEL1_EN_SHIFT 3 23 #define CTRL0_SEL2_SHIFT 4 24 #define CTRL0_SEL2_EN_SHIFT 7 25 #define CTRL0_SEL3_SHIFT 8 26 #define CTRL0_SEL3_EN_SHIFT 11 27 #define CTRL1_FRDDR_FORCE_FINISH BIT(12) 28 #define CTRL2_SEL1_SHIFT 0 29 #define CTRL2_SEL1_EN_SHIFT 4 30 #define CTRL2_SEL2_SHIFT 8 31 #define CTRL2_SEL2_EN_SHIFT 12 32 #define CTRL2_SEL3_SHIFT 16 33 #define CTRL2_SEL3_EN_SHIFT 20 34 35 static int g12a_frddr_dai_prepare(struct snd_pcm_substream *substream, 36 struct snd_soc_dai *dai) 37 { 38 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 39 40 /* Reset the read pointer to the FIFO_INIT_ADDR */ 41 regmap_update_bits(fifo->map, FIFO_CTRL1, 42 CTRL1_FRDDR_FORCE_FINISH, 0); 43 regmap_update_bits(fifo->map, FIFO_CTRL1, 44 CTRL1_FRDDR_FORCE_FINISH, CTRL1_FRDDR_FORCE_FINISH); 45 regmap_update_bits(fifo->map, FIFO_CTRL1, 46 CTRL1_FRDDR_FORCE_FINISH, 0); 47 48 return 0; 49 } 50 51 static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream, 52 struct snd_pcm_hw_params *params, 53 struct snd_soc_dai *dai) 54 { 55 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 56 unsigned int period, depth, val; 57 58 period = params_period_bytes(params); 59 60 /* Trim the FIFO depth if the period is small to improve latency */ 61 depth = min(period, fifo->depth); 62 val = (depth / AXG_FIFO_BURST) - 1; 63 regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH, 64 FIELD_PREP(CTRL1_FRDDR_DEPTH, val)); 65 66 return 0; 67 } 68 69 static int axg_frddr_dai_startup(struct snd_pcm_substream *substream, 70 struct snd_soc_dai *dai) 71 { 72 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 73 int ret; 74 75 /* Enable pclk to access registers and clock the fifo ip */ 76 ret = clk_prepare_enable(fifo->pclk); 77 if (ret) 78 return ret; 79 80 /* Apply single buffer mode to the interface */ 81 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0); 82 83 return 0; 84 } 85 86 static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream, 87 struct snd_soc_dai *dai) 88 { 89 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 90 91 clk_disable_unprepare(fifo->pclk); 92 } 93 94 static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd, 95 struct snd_soc_dai *dai) 96 { 97 return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK); 98 } 99 100 static const struct snd_soc_dai_ops axg_frddr_ops = { 101 .hw_params = axg_frddr_dai_hw_params, 102 .startup = axg_frddr_dai_startup, 103 .shutdown = axg_frddr_dai_shutdown, 104 .pcm_new = axg_frddr_pcm_new, 105 }; 106 107 static struct snd_soc_dai_driver axg_frddr_dai_drv = { 108 .name = "FRDDR", 109 .playback = { 110 .stream_name = "Playback", 111 .channels_min = 1, 112 .channels_max = AXG_FIFO_CH_MAX, 113 .rates = SNDRV_PCM_RATE_CONTINUOUS, 114 .rate_min = 5515, 115 .rate_max = 768000, 116 .formats = AXG_FIFO_FORMATS, 117 }, 118 .ops = &axg_frddr_ops, 119 }; 120 121 static const char * const axg_frddr_sel_texts[] = { 122 "OUT 0", "OUT 1", "OUT 2", "OUT 3", "OUT 4", "OUT 5", "OUT 6", "OUT 7", 123 }; 124 125 static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, 126 axg_frddr_sel_texts); 127 128 static const struct snd_kcontrol_new axg_frddr_out_demux = 129 SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum); 130 131 static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = { 132 SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0, 133 &axg_frddr_out_demux), 134 SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0), 135 SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0), 136 SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0), 137 SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0), 138 SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0), 139 SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0), 140 SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0), 141 SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0), 142 }; 143 144 static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = { 145 { "SINK SEL", NULL, "Playback" }, 146 { "OUT 0", "OUT 0", "SINK SEL" }, 147 { "OUT 1", "OUT 1", "SINK SEL" }, 148 { "OUT 2", "OUT 2", "SINK SEL" }, 149 { "OUT 3", "OUT 3", "SINK SEL" }, 150 { "OUT 4", "OUT 4", "SINK SEL" }, 151 { "OUT 5", "OUT 5", "SINK SEL" }, 152 { "OUT 6", "OUT 6", "SINK SEL" }, 153 { "OUT 7", "OUT 7", "SINK SEL" }, 154 }; 155 156 static const struct snd_soc_component_driver axg_frddr_component_drv = { 157 .dapm_widgets = axg_frddr_dapm_widgets, 158 .num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets), 159 .dapm_routes = axg_frddr_dapm_routes, 160 .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes), 161 .open = axg_fifo_pcm_open, 162 .close = axg_fifo_pcm_close, 163 .hw_params = axg_fifo_pcm_hw_params, 164 .hw_free = axg_fifo_pcm_hw_free, 165 .pointer = axg_fifo_pcm_pointer, 166 .trigger = axg_fifo_pcm_trigger, 167 .legacy_dai_naming = 1, 168 }; 169 170 static const struct axg_fifo_match_data axg_frddr_match_data = { 171 .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), 172 .component_drv = &axg_frddr_component_drv, 173 .dai_drv = &axg_frddr_dai_drv 174 }; 175 176 static const struct snd_soc_dai_ops g12a_frddr_ops = { 177 .prepare = g12a_frddr_dai_prepare, 178 .hw_params = axg_frddr_dai_hw_params, 179 .startup = axg_frddr_dai_startup, 180 .shutdown = axg_frddr_dai_shutdown, 181 .pcm_new = axg_frddr_pcm_new, 182 }; 183 184 static struct snd_soc_dai_driver g12a_frddr_dai_drv = { 185 .name = "FRDDR", 186 .playback = { 187 .stream_name = "Playback", 188 .channels_min = 1, 189 .channels_max = AXG_FIFO_CH_MAX, 190 .rates = SNDRV_PCM_RATE_CONTINUOUS, 191 .rate_min = 5515, 192 .rate_max = 768000, 193 .formats = AXG_FIFO_FORMATS, 194 }, 195 .ops = &g12a_frddr_ops, 196 }; 197 198 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, 199 axg_frddr_sel_texts); 200 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel2_enum, FIFO_CTRL0, CTRL0_SEL2_SHIFT, 201 axg_frddr_sel_texts); 202 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel3_enum, FIFO_CTRL0, CTRL0_SEL3_SHIFT, 203 axg_frddr_sel_texts); 204 205 static const struct snd_kcontrol_new g12a_frddr_out1_demux = 206 SOC_DAPM_ENUM("Output Src 1", g12a_frddr_sel1_enum); 207 static const struct snd_kcontrol_new g12a_frddr_out2_demux = 208 SOC_DAPM_ENUM("Output Src 2", g12a_frddr_sel2_enum); 209 static const struct snd_kcontrol_new g12a_frddr_out3_demux = 210 SOC_DAPM_ENUM("Output Src 3", g12a_frddr_sel3_enum); 211 212 static const struct snd_kcontrol_new g12a_frddr_out1_enable = 213 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0, 214 CTRL0_SEL1_EN_SHIFT, 1, 0); 215 static const struct snd_kcontrol_new g12a_frddr_out2_enable = 216 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0, 217 CTRL0_SEL2_EN_SHIFT, 1, 0); 218 static const struct snd_kcontrol_new g12a_frddr_out3_enable = 219 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0, 220 CTRL0_SEL3_EN_SHIFT, 1, 0); 221 222 static const struct snd_soc_dapm_widget g12a_frddr_dapm_widgets[] = { 223 SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0), 224 SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0), 225 SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0), 226 SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0, 227 &g12a_frddr_out1_enable), 228 SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0, 229 &g12a_frddr_out2_enable), 230 SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0, 231 &g12a_frddr_out3_enable), 232 SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0, 233 &g12a_frddr_out1_demux), 234 SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0, 235 &g12a_frddr_out2_demux), 236 SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0, 237 &g12a_frddr_out3_demux), 238 SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0), 239 SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0), 240 SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0), 241 SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0), 242 SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0), 243 SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0), 244 SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0), 245 SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0), 246 }; 247 248 static const struct snd_soc_dapm_route g12a_frddr_dapm_routes[] = { 249 { "SRC 1", NULL, "Playback" }, 250 { "SRC 2", NULL, "Playback" }, 251 { "SRC 3", NULL, "Playback" }, 252 { "SRC 1 EN", "Switch", "SRC 1" }, 253 { "SRC 2 EN", "Switch", "SRC 2" }, 254 { "SRC 3 EN", "Switch", "SRC 3" }, 255 { "SINK 1 SEL", NULL, "SRC 1 EN" }, 256 { "SINK 2 SEL", NULL, "SRC 2 EN" }, 257 { "SINK 3 SEL", NULL, "SRC 3 EN" }, 258 { "OUT 0", "OUT 0", "SINK 1 SEL" }, 259 { "OUT 1", "OUT 1", "SINK 1 SEL" }, 260 { "OUT 2", "OUT 2", "SINK 1 SEL" }, 261 { "OUT 3", "OUT 3", "SINK 1 SEL" }, 262 { "OUT 4", "OUT 4", "SINK 1 SEL" }, 263 { "OUT 5", "OUT 5", "SINK 1 SEL" }, 264 { "OUT 6", "OUT 6", "SINK 1 SEL" }, 265 { "OUT 7", "OUT 7", "SINK 1 SEL" }, 266 { "OUT 0", "OUT 0", "SINK 2 SEL" }, 267 { "OUT 1", "OUT 1", "SINK 2 SEL" }, 268 { "OUT 2", "OUT 2", "SINK 2 SEL" }, 269 { "OUT 3", "OUT 3", "SINK 2 SEL" }, 270 { "OUT 4", "OUT 4", "SINK 2 SEL" }, 271 { "OUT 5", "OUT 5", "SINK 2 SEL" }, 272 { "OUT 6", "OUT 6", "SINK 2 SEL" }, 273 { "OUT 7", "OUT 7", "SINK 2 SEL" }, 274 { "OUT 0", "OUT 0", "SINK 3 SEL" }, 275 { "OUT 1", "OUT 1", "SINK 3 SEL" }, 276 { "OUT 2", "OUT 2", "SINK 3 SEL" }, 277 { "OUT 3", "OUT 3", "SINK 3 SEL" }, 278 { "OUT 4", "OUT 4", "SINK 3 SEL" }, 279 { "OUT 5", "OUT 5", "SINK 3 SEL" }, 280 { "OUT 6", "OUT 6", "SINK 3 SEL" }, 281 { "OUT 7", "OUT 7", "SINK 3 SEL" }, 282 }; 283 284 static const struct snd_soc_component_driver g12a_frddr_component_drv = { 285 .dapm_widgets = g12a_frddr_dapm_widgets, 286 .num_dapm_widgets = ARRAY_SIZE(g12a_frddr_dapm_widgets), 287 .dapm_routes = g12a_frddr_dapm_routes, 288 .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), 289 .open = axg_fifo_pcm_open, 290 .close = axg_fifo_pcm_close, 291 .hw_params = g12a_fifo_pcm_hw_params, 292 .hw_free = axg_fifo_pcm_hw_free, 293 .pointer = axg_fifo_pcm_pointer, 294 .trigger = axg_fifo_pcm_trigger, 295 .legacy_dai_naming = 1, 296 }; 297 298 static const struct axg_fifo_match_data g12a_frddr_match_data = { 299 .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), 300 .component_drv = &g12a_frddr_component_drv, 301 .dai_drv = &g12a_frddr_dai_drv 302 }; 303 304 /* On SM1, the output selection in on CTRL2 */ 305 static const struct snd_kcontrol_new sm1_frddr_out1_enable = 306 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2, 307 CTRL2_SEL1_EN_SHIFT, 1, 0); 308 static const struct snd_kcontrol_new sm1_frddr_out2_enable = 309 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2, 310 CTRL2_SEL2_EN_SHIFT, 1, 0); 311 static const struct snd_kcontrol_new sm1_frddr_out3_enable = 312 SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2, 313 CTRL2_SEL3_EN_SHIFT, 1, 0); 314 315 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel1_enum, FIFO_CTRL2, CTRL2_SEL1_SHIFT, 316 axg_frddr_sel_texts); 317 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel2_enum, FIFO_CTRL2, CTRL2_SEL2_SHIFT, 318 axg_frddr_sel_texts); 319 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel3_enum, FIFO_CTRL2, CTRL2_SEL3_SHIFT, 320 axg_frddr_sel_texts); 321 322 static const struct snd_kcontrol_new sm1_frddr_out1_demux = 323 SOC_DAPM_ENUM("Output Src 1", sm1_frddr_sel1_enum); 324 static const struct snd_kcontrol_new sm1_frddr_out2_demux = 325 SOC_DAPM_ENUM("Output Src 2", sm1_frddr_sel2_enum); 326 static const struct snd_kcontrol_new sm1_frddr_out3_demux = 327 SOC_DAPM_ENUM("Output Src 3", sm1_frddr_sel3_enum); 328 329 static const struct snd_soc_dapm_widget sm1_frddr_dapm_widgets[] = { 330 SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0), 331 SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0), 332 SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0), 333 SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0, 334 &sm1_frddr_out1_enable), 335 SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0, 336 &sm1_frddr_out2_enable), 337 SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0, 338 &sm1_frddr_out3_enable), 339 SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0, 340 &sm1_frddr_out1_demux), 341 SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0, 342 &sm1_frddr_out2_demux), 343 SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0, 344 &sm1_frddr_out3_demux), 345 SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0), 346 SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0), 347 SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0), 348 SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0), 349 SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0), 350 SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0), 351 SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0), 352 SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0), 353 }; 354 355 static const struct snd_soc_component_driver sm1_frddr_component_drv = { 356 .dapm_widgets = sm1_frddr_dapm_widgets, 357 .num_dapm_widgets = ARRAY_SIZE(sm1_frddr_dapm_widgets), 358 .dapm_routes = g12a_frddr_dapm_routes, 359 .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), 360 .open = axg_fifo_pcm_open, 361 .close = axg_fifo_pcm_close, 362 .hw_params = g12a_fifo_pcm_hw_params, 363 .hw_free = axg_fifo_pcm_hw_free, 364 .pointer = axg_fifo_pcm_pointer, 365 .trigger = axg_fifo_pcm_trigger, 366 .legacy_dai_naming = 1, 367 }; 368 369 static const struct axg_fifo_match_data sm1_frddr_match_data = { 370 .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), 371 .component_drv = &sm1_frddr_component_drv, 372 .dai_drv = &g12a_frddr_dai_drv 373 }; 374 375 static const struct of_device_id axg_frddr_of_match[] = { 376 { 377 .compatible = "amlogic,axg-frddr", 378 .data = &axg_frddr_match_data, 379 }, { 380 .compatible = "amlogic,g12a-frddr", 381 .data = &g12a_frddr_match_data, 382 }, { 383 .compatible = "amlogic,sm1-frddr", 384 .data = &sm1_frddr_match_data, 385 }, {} 386 }; 387 MODULE_DEVICE_TABLE(of, axg_frddr_of_match); 388 389 static struct platform_driver axg_frddr_pdrv = { 390 .probe = axg_fifo_probe, 391 .driver = { 392 .name = "axg-frddr", 393 .of_match_table = axg_frddr_of_match, 394 }, 395 }; 396 module_platform_driver(axg_frddr_pdrv); 397 398 MODULE_DESCRIPTION("Amlogic AXG/G12A playback fifo driver"); 399 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 400 MODULE_LICENSE("GPL v2"); 401
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.