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 /* This driver implements the frontend capture DAI of AXG based SoCs */ 7 8 #include <linux/clk.h> 9 #include <linux/regmap.h> 10 #include <linux/module.h> 11 #include <linux/of_platform.h> 12 #include <sound/pcm_params.h> 13 #include <sound/soc.h> 14 #include <sound/soc-dai.h> 15 16 #include "axg-fifo.h" 17 18 #define CTRL0_TODDR_SEL_RESAMPLE BIT(30) 19 #define CTRL0_TODDR_EXT_SIGNED BIT(29) 20 #define CTRL0_TODDR_PP_MODE BIT(28) 21 #define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) 22 #define CTRL0_TODDR_TYPE(x) ((x) << 13) 23 #define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) 24 #define CTRL0_TODDR_MSB_POS(x) ((x) << 8) 25 #define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) 26 #define CTRL0_TODDR_LSB_POS(x) ((x) << 3) 27 #define CTRL1_TODDR_FORCE_FINISH BIT(25) 28 29 #define TODDR_MSB_POS 31 30 31 static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, 32 struct snd_soc_dai *dai) 33 { 34 return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); 35 } 36 37 static int g12a_toddr_dai_prepare(struct snd_pcm_substream *substream, 38 struct snd_soc_dai *dai) 39 { 40 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 41 42 /* Reset the write pointer to the FIFO_INIT_ADDR */ 43 regmap_update_bits(fifo->map, FIFO_CTRL1, 44 CTRL1_TODDR_FORCE_FINISH, 0); 45 regmap_update_bits(fifo->map, FIFO_CTRL1, 46 CTRL1_TODDR_FORCE_FINISH, CTRL1_TODDR_FORCE_FINISH); 47 regmap_update_bits(fifo->map, FIFO_CTRL1, 48 CTRL1_TODDR_FORCE_FINISH, 0); 49 50 return 0; 51 } 52 53 static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, 54 struct snd_pcm_hw_params *params, 55 struct snd_soc_dai *dai) 56 { 57 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 58 unsigned int type, width; 59 60 switch (params_physical_width(params)) { 61 case 8: 62 type = 0; /* 8 samples of 8 bits */ 63 break; 64 case 16: 65 type = 2; /* 4 samples of 16 bits - right justified */ 66 break; 67 case 32: 68 type = 4; /* 2 samples of 32 bits - right justified */ 69 break; 70 default: 71 return -EINVAL; 72 } 73 74 width = params_width(params); 75 76 regmap_update_bits(fifo->map, FIFO_CTRL0, 77 CTRL0_TODDR_TYPE_MASK | 78 CTRL0_TODDR_MSB_POS_MASK | 79 CTRL0_TODDR_LSB_POS_MASK, 80 CTRL0_TODDR_TYPE(type) | 81 CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | 82 CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); 83 84 return 0; 85 } 86 87 static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, 88 struct snd_soc_dai *dai) 89 { 90 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 91 unsigned int fifo_threshold; 92 int ret; 93 94 /* Enable pclk to access registers and clock the fifo ip */ 95 ret = clk_prepare_enable(fifo->pclk); 96 if (ret) 97 return ret; 98 99 /* Select orginal data - resampling not supported ATM */ 100 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); 101 102 /* Only signed format are supported ATM */ 103 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, 104 CTRL0_TODDR_EXT_SIGNED); 105 106 /* Apply single buffer mode to the interface */ 107 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); 108 109 /* TODDR does not have a configurable fifo depth */ 110 fifo_threshold = AXG_FIFO_MIN_CNT - 1; 111 regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, 112 CTRL1_THRESHOLD(fifo_threshold)); 113 114 return 0; 115 } 116 117 static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, 118 struct snd_soc_dai *dai) 119 { 120 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 121 122 clk_disable_unprepare(fifo->pclk); 123 } 124 125 static const struct snd_soc_dai_ops axg_toddr_ops = { 126 .hw_params = axg_toddr_dai_hw_params, 127 .startup = axg_toddr_dai_startup, 128 .shutdown = axg_toddr_dai_shutdown, 129 }; 130 131 static struct snd_soc_dai_driver axg_toddr_dai_drv = { 132 .name = "TODDR", 133 .capture = { 134 .stream_name = "Capture", 135 .channels_min = 1, 136 .channels_max = AXG_FIFO_CH_MAX, 137 .rates = AXG_FIFO_RATES, 138 .formats = AXG_FIFO_FORMATS, 139 }, 140 .ops = &axg_toddr_ops, 141 .pcm_new = axg_toddr_pcm_new, 142 }; 143 144 static const char * const axg_toddr_sel_texts[] = { 145 "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" 146 }; 147 148 static const unsigned int axg_toddr_sel_values[] = { 149 0, 1, 2, 3, 4, 6 150 }; 151 152 static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, 153 CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, 154 axg_toddr_sel_texts, axg_toddr_sel_values); 155 156 static const struct snd_kcontrol_new axg_toddr_in_mux = 157 SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); 158 159 static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { 160 SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), 161 SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), 162 SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), 163 SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), 164 SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), 165 SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), 166 SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), 167 }; 168 169 static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { 170 { "Capture", NULL, "SRC SEL" }, 171 { "SRC SEL", "IN 0", "IN 0" }, 172 { "SRC SEL", "IN 1", "IN 1" }, 173 { "SRC SEL", "IN 2", "IN 2" }, 174 { "SRC SEL", "IN 3", "IN 3" }, 175 { "SRC SEL", "IN 4", "IN 4" }, 176 { "SRC SEL", "IN 6", "IN 6" }, 177 }; 178 179 static const struct snd_soc_component_driver axg_toddr_component_drv = { 180 .dapm_widgets = axg_toddr_dapm_widgets, 181 .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), 182 .dapm_routes = axg_toddr_dapm_routes, 183 .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), 184 .ops = &axg_fifo_pcm_ops 185 }; 186 187 static const struct axg_fifo_match_data axg_toddr_match_data = { 188 .component_drv = &axg_toddr_component_drv, 189 .dai_drv = &axg_toddr_dai_drv 190 }; 191 192 static const struct snd_soc_dai_ops g12a_toddr_ops = { 193 .prepare = g12a_toddr_dai_prepare, 194 .hw_params = axg_toddr_dai_hw_params, 195 .startup = axg_toddr_dai_startup, 196 .shutdown = axg_toddr_dai_shutdown, 197 }; 198 199 static struct snd_soc_dai_driver g12a_toddr_dai_drv = { 200 .name = "TODDR", 201 .capture = { 202 .stream_name = "Capture", 203 .channels_min = 1, 204 .channels_max = AXG_FIFO_CH_MAX, 205 .rates = AXG_FIFO_RATES, 206 .formats = AXG_FIFO_FORMATS, 207 }, 208 .ops = &g12a_toddr_ops, 209 .pcm_new = axg_toddr_pcm_new, 210 }; 211 212 static const struct snd_soc_component_driver g12a_toddr_component_drv = { 213 .dapm_widgets = axg_toddr_dapm_widgets, 214 .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), 215 .dapm_routes = axg_toddr_dapm_routes, 216 .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), 217 .ops = &g12a_fifo_pcm_ops 218 }; 219 220 static const struct axg_fifo_match_data g12a_toddr_match_data = { 221 .component_drv = &g12a_toddr_component_drv, 222 .dai_drv = &g12a_toddr_dai_drv 223 }; 224 225 static const struct of_device_id axg_toddr_of_match[] = { 226 { 227 .compatible = "amlogic,axg-toddr", 228 .data = &axg_toddr_match_data, 229 }, { 230 .compatible = "amlogic,g12a-toddr", 231 .data = &g12a_toddr_match_data, 232 }, {} 233 }; 234 MODULE_DEVICE_TABLE(of, axg_toddr_of_match); 235 236 static struct platform_driver axg_toddr_pdrv = { 237 .probe = axg_fifo_probe, 238 .driver = { 239 .name = "axg-toddr", 240 .of_match_table = axg_toddr_of_match, 241 }, 242 }; 243 module_platform_driver(axg_toddr_pdrv); 244 245 MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); 246 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 247 MODULE_LICENSE("GPL v2"); 248