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 28 #define TODDR_MSB_POS 31 29 30 static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, 31 struct snd_soc_dai *dai) 32 { 33 return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); 34 } 35 36 static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, 37 struct snd_pcm_hw_params *params, 38 struct snd_soc_dai *dai) 39 { 40 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 41 unsigned int type, width; 42 43 switch (params_physical_width(params)) { 44 case 8: 45 type = 0; /* 8 samples of 8 bits */ 46 break; 47 case 16: 48 type = 2; /* 4 samples of 16 bits - right justified */ 49 break; 50 case 32: 51 type = 4; /* 2 samples of 32 bits - right justified */ 52 break; 53 default: 54 return -EINVAL; 55 } 56 57 width = params_width(params); 58 59 regmap_update_bits(fifo->map, FIFO_CTRL0, 60 CTRL0_TODDR_TYPE_MASK | 61 CTRL0_TODDR_MSB_POS_MASK | 62 CTRL0_TODDR_LSB_POS_MASK, 63 CTRL0_TODDR_TYPE(type) | 64 CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | 65 CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); 66 67 return 0; 68 } 69 70 static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, 71 struct snd_soc_dai *dai) 72 { 73 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 74 unsigned int fifo_threshold; 75 int ret; 76 77 /* Enable pclk to access registers and clock the fifo ip */ 78 ret = clk_prepare_enable(fifo->pclk); 79 if (ret) 80 return ret; 81 82 /* Select orginal data - resampling not supported ATM */ 83 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); 84 85 /* Only signed format are supported ATM */ 86 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, 87 CTRL0_TODDR_EXT_SIGNED); 88 89 /* Apply single buffer mode to the interface */ 90 regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); 91 92 /* TODDR does not have a configurable fifo depth */ 93 fifo_threshold = AXG_FIFO_MIN_CNT - 1; 94 regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, 95 CTRL1_THRESHOLD(fifo_threshold)); 96 97 return 0; 98 } 99 100 static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, 101 struct snd_soc_dai *dai) 102 { 103 struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 104 105 clk_disable_unprepare(fifo->pclk); 106 } 107 108 static const struct snd_soc_dai_ops axg_toddr_ops = { 109 .hw_params = axg_toddr_dai_hw_params, 110 .startup = axg_toddr_dai_startup, 111 .shutdown = axg_toddr_dai_shutdown, 112 }; 113 114 static struct snd_soc_dai_driver axg_toddr_dai_drv = { 115 .name = "TODDR", 116 .capture = { 117 .stream_name = "Capture", 118 .channels_min = 1, 119 .channels_max = AXG_FIFO_CH_MAX, 120 .rates = AXG_FIFO_RATES, 121 .formats = AXG_FIFO_FORMATS, 122 }, 123 .ops = &axg_toddr_ops, 124 .pcm_new = axg_toddr_pcm_new, 125 }; 126 127 static const char * const axg_toddr_sel_texts[] = { 128 "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" 129 }; 130 131 static const unsigned int axg_toddr_sel_values[] = { 132 0, 1, 2, 3, 4, 6 133 }; 134 135 static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, 136 CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, 137 axg_toddr_sel_texts, axg_toddr_sel_values); 138 139 static const struct snd_kcontrol_new axg_toddr_in_mux = 140 SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); 141 142 static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { 143 SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), 144 SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), 145 SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), 146 SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), 147 SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), 148 SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), 149 SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), 150 }; 151 152 static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { 153 { "Capture", NULL, "SRC SEL" }, 154 { "SRC SEL", "IN 0", "IN 0" }, 155 { "SRC SEL", "IN 1", "IN 1" }, 156 { "SRC SEL", "IN 2", "IN 2" }, 157 { "SRC SEL", "IN 3", "IN 3" }, 158 { "SRC SEL", "IN 4", "IN 4" }, 159 { "SRC SEL", "IN 6", "IN 6" }, 160 }; 161 162 static const struct snd_soc_component_driver axg_toddr_component_drv = { 163 .dapm_widgets = axg_toddr_dapm_widgets, 164 .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), 165 .dapm_routes = axg_toddr_dapm_routes, 166 .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), 167 .ops = &axg_fifo_pcm_ops 168 }; 169 170 static const struct axg_fifo_match_data axg_toddr_match_data = { 171 .component_drv = &axg_toddr_component_drv, 172 .dai_drv = &axg_toddr_dai_drv 173 }; 174 175 static const struct of_device_id axg_toddr_of_match[] = { 176 { 177 .compatible = "amlogic,axg-toddr", 178 .data = &axg_toddr_match_data, 179 }, {} 180 }; 181 MODULE_DEVICE_TABLE(of, axg_toddr_of_match); 182 183 static struct platform_driver axg_toddr_pdrv = { 184 .probe = axg_fifo_probe, 185 .driver = { 186 .name = "axg-toddr", 187 .of_match_table = axg_toddr_of_match, 188 }, 189 }; 190 module_platform_driver(axg_toddr_pdrv); 191 192 MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); 193 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 194 MODULE_LICENSE("GPL v2"); 195