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