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