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