1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (c) 2019 BayLibre, SAS. 4 // Author: Jerome Brunet <jbrunet@baylibre.com> 5 6 #include <linux/bitfield.h> 7 #include <linux/clk.h> 8 #include <linux/module.h> 9 #include <sound/pcm_params.h> 10 #include <linux/regmap.h> 11 #include <sound/soc.h> 12 #include <sound/soc-dai.h> 13 14 #include <dt-bindings/sound/meson-g12a-tohdmitx.h> 15 16 #define G12A_TOHDMITX_DRV_NAME "g12a-tohdmitx" 17 18 #define TOHDMITX_CTRL0 0x0 19 #define CTRL0_ENABLE_SHIFT 31 20 #define CTRL0_I2S_DAT_SEL GENMASK(13, 12) 21 #define CTRL0_I2S_LRCLK_SEL GENMASK(9, 8) 22 #define CTRL0_I2S_BLK_CAP_INV BIT(7) 23 #define CTRL0_I2S_BCLK_O_INV BIT(6) 24 #define CTRL0_I2S_BCLK_SEL GENMASK(5, 4) 25 #define CTRL0_SPDIF_CLK_CAP_INV BIT(3) 26 #define CTRL0_SPDIF_CLK_O_INV BIT(2) 27 #define CTRL0_SPDIF_SEL BIT(1) 28 #define CTRL0_SPDIF_CLK_SEL BIT(0) 29 30 struct g12a_tohdmitx_input { 31 struct snd_soc_pcm_stream params; 32 unsigned int fmt; 33 }; 34 35 static struct snd_soc_dapm_widget * 36 g12a_tohdmitx_get_input(struct snd_soc_dapm_widget *w) 37 { 38 struct snd_soc_dapm_path *p = NULL; 39 struct snd_soc_dapm_widget *in; 40 41 snd_soc_dapm_widget_for_each_source_path(w, p) { 42 if (!p->connect) 43 continue; 44 45 /* Check that we still are in the same component */ 46 if (snd_soc_dapm_to_component(w->dapm) != 47 snd_soc_dapm_to_component(p->source->dapm)) 48 continue; 49 50 if (p->source->id == snd_soc_dapm_dai_in) 51 return p->source; 52 53 in = g12a_tohdmitx_get_input(p->source); 54 if (in) 55 return in; 56 } 57 58 return NULL; 59 } 60 61 static struct g12a_tohdmitx_input * 62 g12a_tohdmitx_get_input_data(struct snd_soc_dapm_widget *w) 63 { 64 struct snd_soc_dapm_widget *in = 65 g12a_tohdmitx_get_input(w); 66 struct snd_soc_dai *dai; 67 68 if (WARN_ON(!in)) 69 return NULL; 70 71 dai = in->priv; 72 73 return dai->playback_dma_data; 74 } 75 76 static const char * const g12a_tohdmitx_i2s_mux_texts[] = { 77 "I2S A", "I2S B", "I2S C", 78 }; 79 80 static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_i2s_mux_enum, 81 g12a_tohdmitx_i2s_mux_texts); 82 83 static int g12a_tohdmitx_get_input_val(struct snd_soc_component *component, 84 unsigned int mask) 85 { 86 unsigned int val; 87 88 snd_soc_component_read(component, TOHDMITX_CTRL0, &val); 89 return (val & mask) >> __ffs(mask); 90 } 91 92 static int g12a_tohdmitx_i2s_mux_get_enum(struct snd_kcontrol *kcontrol, 93 struct snd_ctl_elem_value *ucontrol) 94 { 95 struct snd_soc_component *component = 96 snd_soc_dapm_kcontrol_component(kcontrol); 97 98 ucontrol->value.enumerated.item[0] = 99 g12a_tohdmitx_get_input_val(component, CTRL0_I2S_DAT_SEL); 100 101 return 0; 102 } 103 104 static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol, 105 struct snd_ctl_elem_value *ucontrol) 106 { 107 struct snd_soc_component *component = 108 snd_soc_dapm_kcontrol_component(kcontrol); 109 struct snd_soc_dapm_context *dapm = 110 snd_soc_dapm_kcontrol_dapm(kcontrol); 111 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 112 unsigned int mux = ucontrol->value.enumerated.item[0]; 113 unsigned int val = g12a_tohdmitx_get_input_val(component, 114 CTRL0_I2S_DAT_SEL); 115 116 /* Force disconnect of the mux while updating */ 117 if (val != mux) 118 snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 119 120 snd_soc_component_update_bits(component, TOHDMITX_CTRL0, 121 CTRL0_I2S_DAT_SEL | 122 CTRL0_I2S_LRCLK_SEL | 123 CTRL0_I2S_BCLK_SEL, 124 FIELD_PREP(CTRL0_I2S_DAT_SEL, mux) | 125 FIELD_PREP(CTRL0_I2S_LRCLK_SEL, mux) | 126 FIELD_PREP(CTRL0_I2S_BCLK_SEL, mux)); 127 128 snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 129 130 return 0; 131 } 132 133 static const struct snd_kcontrol_new g12a_tohdmitx_i2s_mux = 134 SOC_DAPM_ENUM_EXT("I2S Source", g12a_tohdmitx_i2s_mux_enum, 135 g12a_tohdmitx_i2s_mux_get_enum, 136 g12a_tohdmitx_i2s_mux_put_enum); 137 138 static const char * const g12a_tohdmitx_spdif_mux_texts[] = { 139 "SPDIF A", "SPDIF B", 140 }; 141 142 static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_spdif_mux_enum, 143 g12a_tohdmitx_spdif_mux_texts); 144 145 static int g12a_tohdmitx_spdif_mux_get_enum(struct snd_kcontrol *kcontrol, 146 struct snd_ctl_elem_value *ucontrol) 147 { 148 struct snd_soc_component *component = 149 snd_soc_dapm_kcontrol_component(kcontrol); 150 151 ucontrol->value.enumerated.item[0] = 152 g12a_tohdmitx_get_input_val(component, CTRL0_SPDIF_SEL); 153 154 return 0; 155 } 156 157 static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, 158 struct snd_ctl_elem_value *ucontrol) 159 { 160 struct snd_soc_component *component = 161 snd_soc_dapm_kcontrol_component(kcontrol); 162 struct snd_soc_dapm_context *dapm = 163 snd_soc_dapm_kcontrol_dapm(kcontrol); 164 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 165 unsigned int mux = ucontrol->value.enumerated.item[0]; 166 unsigned int val = g12a_tohdmitx_get_input_val(component, 167 CTRL0_SPDIF_SEL); 168 169 /* Force disconnect of the mux while updating */ 170 if (val != mux) 171 snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 172 173 snd_soc_component_update_bits(component, TOHDMITX_CTRL0, 174 CTRL0_SPDIF_SEL | 175 CTRL0_SPDIF_CLK_SEL, 176 FIELD_PREP(CTRL0_SPDIF_SEL, mux) | 177 FIELD_PREP(CTRL0_SPDIF_CLK_SEL, mux)); 178 179 snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 180 181 return 0; 182 } 183 184 static const struct snd_kcontrol_new g12a_tohdmitx_spdif_mux = 185 SOC_DAPM_ENUM_EXT("SPDIF Source", g12a_tohdmitx_spdif_mux_enum, 186 g12a_tohdmitx_spdif_mux_get_enum, 187 g12a_tohdmitx_spdif_mux_put_enum); 188 189 static const struct snd_kcontrol_new g12a_tohdmitx_out_enable = 190 SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOHDMITX_CTRL0, 191 CTRL0_ENABLE_SHIFT, 1, 0); 192 193 static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = { 194 SND_SOC_DAPM_MUX("I2S SRC", SND_SOC_NOPM, 0, 0, 195 &g12a_tohdmitx_i2s_mux), 196 SND_SOC_DAPM_SWITCH("I2S OUT EN", SND_SOC_NOPM, 0, 0, 197 &g12a_tohdmitx_out_enable), 198 SND_SOC_DAPM_MUX("SPDIF SRC", SND_SOC_NOPM, 0, 0, 199 &g12a_tohdmitx_spdif_mux), 200 SND_SOC_DAPM_SWITCH("SPDIF OUT EN", SND_SOC_NOPM, 0, 0, 201 &g12a_tohdmitx_out_enable), 202 }; 203 204 static int g12a_tohdmitx_input_probe(struct snd_soc_dai *dai) 205 { 206 struct g12a_tohdmitx_input *data; 207 208 data = kzalloc(sizeof(*data), GFP_KERNEL); 209 if (!data) 210 return -ENOMEM; 211 212 dai->playback_dma_data = data; 213 return 0; 214 } 215 216 static int g12a_tohdmitx_input_remove(struct snd_soc_dai *dai) 217 { 218 kfree(dai->playback_dma_data); 219 return 0; 220 } 221 222 static int g12a_tohdmitx_input_hw_params(struct snd_pcm_substream *substream, 223 struct snd_pcm_hw_params *params, 224 struct snd_soc_dai *dai) 225 { 226 struct g12a_tohdmitx_input *data = dai->playback_dma_data; 227 228 data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); 229 data->params.rate_min = params_rate(params); 230 data->params.rate_max = params_rate(params); 231 data->params.formats = 1 << params_format(params); 232 data->params.channels_min = params_channels(params); 233 data->params.channels_max = params_channels(params); 234 data->params.sig_bits = dai->driver->playback.sig_bits; 235 236 return 0; 237 } 238 239 240 static int g12a_tohdmitx_input_set_fmt(struct snd_soc_dai *dai, 241 unsigned int fmt) 242 { 243 struct g12a_tohdmitx_input *data = dai->playback_dma_data; 244 245 /* Save the source stream format for the downstream link */ 246 data->fmt = fmt; 247 return 0; 248 } 249 250 static int g12a_tohdmitx_output_startup(struct snd_pcm_substream *substream, 251 struct snd_soc_dai *dai) 252 { 253 struct snd_soc_pcm_runtime *rtd = substream->private_data; 254 struct g12a_tohdmitx_input *in_data = 255 g12a_tohdmitx_get_input_data(dai->capture_widget); 256 257 if (!in_data) 258 return -ENODEV; 259 260 if (WARN_ON(!rtd->dai_link->params)) { 261 dev_warn(dai->dev, "codec2codec link expected\n"); 262 return -EINVAL; 263 } 264 265 /* Replace link params with the input params */ 266 rtd->dai_link->params = &in_data->params; 267 268 if (!in_data->fmt) 269 return 0; 270 271 return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); 272 } 273 274 static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = { 275 .hw_params = g12a_tohdmitx_input_hw_params, 276 .set_fmt = g12a_tohdmitx_input_set_fmt, 277 }; 278 279 static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { 280 .startup = g12a_tohdmitx_output_startup, 281 }; 282 283 #define TOHDMITX_SPDIF_FORMATS \ 284 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 285 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) 286 287 #define TOHDMITX_I2S_FORMATS \ 288 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 289 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ 290 SNDRV_PCM_FMTBIT_S32_LE) 291 292 #define TOHDMITX_STREAM(xname, xsuffix, xfmt, xchmax) \ 293 { \ 294 .stream_name = xname " " xsuffix, \ 295 .channels_min = 1, \ 296 .channels_max = (xchmax), \ 297 .rate_min = 8000, \ 298 .rate_max = 192000, \ 299 .formats = (xfmt), \ 300 } 301 302 #define TOHDMITX_IN(xname, xid, xfmt, xchmax) { \ 303 .name = xname, \ 304 .id = (xid), \ 305 .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \ 306 .ops = &g12a_tohdmitx_input_ops, \ 307 .probe = g12a_tohdmitx_input_probe, \ 308 .remove = g12a_tohdmitx_input_remove, \ 309 } 310 311 #define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \ 312 .name = xname, \ 313 .id = (xid), \ 314 .capture = TOHDMITX_STREAM(xname, "Capture", xfmt, xchmax), \ 315 .ops = &g12a_tohdmitx_output_ops, \ 316 } 317 318 static struct snd_soc_dai_driver g12a_tohdmitx_dai_drv[] = { 319 TOHDMITX_IN("I2S IN A", TOHDMITX_I2S_IN_A, 320 TOHDMITX_I2S_FORMATS, 8), 321 TOHDMITX_IN("I2S IN B", TOHDMITX_I2S_IN_B, 322 TOHDMITX_I2S_FORMATS, 8), 323 TOHDMITX_IN("I2S IN C", TOHDMITX_I2S_IN_C, 324 TOHDMITX_I2S_FORMATS, 8), 325 TOHDMITX_OUT("I2S OUT", TOHDMITX_I2S_OUT, 326 TOHDMITX_I2S_FORMATS, 8), 327 TOHDMITX_IN("SPDIF IN A", TOHDMITX_SPDIF_IN_A, 328 TOHDMITX_SPDIF_FORMATS, 2), 329 TOHDMITX_IN("SPDIF IN B", TOHDMITX_SPDIF_IN_B, 330 TOHDMITX_SPDIF_FORMATS, 2), 331 TOHDMITX_OUT("SPDIF OUT", TOHDMITX_SPDIF_OUT, 332 TOHDMITX_SPDIF_FORMATS, 2), 333 }; 334 335 static int g12a_tohdmi_component_probe(struct snd_soc_component *c) 336 { 337 /* Initialize the static clock parameters */ 338 return snd_soc_component_write(c, TOHDMITX_CTRL0, 339 CTRL0_I2S_BLK_CAP_INV | CTRL0_SPDIF_CLK_CAP_INV); 340 } 341 342 static const struct snd_soc_dapm_route g12a_tohdmitx_routes[] = { 343 { "I2S SRC", "I2S A", "I2S IN A Playback" }, 344 { "I2S SRC", "I2S B", "I2S IN B Playback" }, 345 { "I2S SRC", "I2S C", "I2S IN C Playback" }, 346 { "I2S OUT EN", "Switch", "I2S SRC" }, 347 { "I2S OUT Capture", NULL, "I2S OUT EN" }, 348 { "SPDIF SRC", "SPDIF A", "SPDIF IN A Playback" }, 349 { "SPDIF SRC", "SPDIF B", "SPDIF IN B Playback" }, 350 { "SPDIF OUT EN", "Switch", "SPDIF SRC" }, 351 { "SPDIF OUT Capture", NULL, "SPDIF OUT EN" }, 352 }; 353 354 static const struct snd_soc_component_driver g12a_tohdmitx_component_drv = { 355 .probe = g12a_tohdmi_component_probe, 356 .dapm_widgets = g12a_tohdmitx_widgets, 357 .num_dapm_widgets = ARRAY_SIZE(g12a_tohdmitx_widgets), 358 .dapm_routes = g12a_tohdmitx_routes, 359 .num_dapm_routes = ARRAY_SIZE(g12a_tohdmitx_routes), 360 .endianness = 1, 361 .non_legacy_dai_naming = 1, 362 }; 363 364 static const struct regmap_config g12a_tohdmitx_regmap_cfg = { 365 .reg_bits = 32, 366 .val_bits = 32, 367 .reg_stride = 4, 368 }; 369 370 static const struct of_device_id g12a_tohdmitx_of_match[] = { 371 { .compatible = "amlogic,g12a-tohdmitx", }, 372 {} 373 }; 374 MODULE_DEVICE_TABLE(of, g12a_tohdmitx_of_match); 375 376 static int g12a_tohdmitx_probe(struct platform_device *pdev) 377 { 378 struct device *dev = &pdev->dev; 379 void __iomem *regs; 380 struct regmap *map; 381 382 regs = devm_platform_ioremap_resource(pdev, 0); 383 if (IS_ERR(regs)) 384 return PTR_ERR(regs); 385 386 map = devm_regmap_init_mmio(dev, regs, &g12a_tohdmitx_regmap_cfg); 387 if (IS_ERR(map)) { 388 dev_err(dev, "failed to init regmap: %ld\n", 389 PTR_ERR(map)); 390 return PTR_ERR(map); 391 } 392 393 return devm_snd_soc_register_component(dev, 394 &g12a_tohdmitx_component_drv, g12a_tohdmitx_dai_drv, 395 ARRAY_SIZE(g12a_tohdmitx_dai_drv)); 396 } 397 398 static struct platform_driver g12a_tohdmitx_pdrv = { 399 .driver = { 400 .name = G12A_TOHDMITX_DRV_NAME, 401 .of_match_table = g12a_tohdmitx_of_match, 402 }, 403 .probe = g12a_tohdmitx_probe, 404 }; 405 module_platform_driver(g12a_tohdmitx_pdrv); 406 407 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 408 MODULE_DESCRIPTION("Amlogic G12a To HDMI Tx Control Codec Driver"); 409 MODULE_LICENSE("GPL v2"); 410