| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From e62741891f6901b5219eacdf60835cac9beb7bae Mon Sep 17 00:00:00 2001 |
| 2 | From: Viorel Suman <viorel.suman@nxp.com> |
| 3 | Date: Wed, 21 Nov 2018 16:09:44 +0200 |
| 4 | Subject: [PATCH] MLK-20328-1: ASoC: fsl_sai: map number of pins to dataline |
| 5 | masks |
| 6 | |
| 7 | The patch enable mapping the number of pins required to play or record |
| 8 | a specific number of channels to a specific dataline mask. |
| 9 | |
| 10 | Three consequent elements in "fsl,dataline" and "fsl,dataline,dsd" defines a |
| 11 | particular mapping, for instance for: fsl,dataline = "0 0xff 0xff 2 0x11 0x11" |
| 12 | there are two mappings defined: |
| 13 | |
| 14 | default (0 pins) "rx" and "tx" dataline masks: 0 0xff 0xff |
| 15 | 2 pins "rx" and "tx" dataline masks: 2 0x11 0x11 |
| 16 | |
| 17 | In case if property is missing, then default value "0 0x1 0x1" is considered. |
| 18 | |
| 19 | Signed-off-by: Viorel Suman <viorel.suman@nxp.com> |
| 20 | --- |
| 21 | sound/soc/fsl/fsl_sai.c | 227 ++++++++++++++++++++++++++++++------------------ |
| 22 | sound/soc/fsl/fsl_sai.h | 16 +++- |
| 23 | 2 files changed, 153 insertions(+), 90 deletions(-) |
| 24 | |
| 25 | --- a/sound/soc/fsl/fsl_sai.c |
| 26 | +++ b/sound/soc/fsl/fsl_sai.c |
| 27 | @@ -621,17 +621,35 @@ static int fsl_sai_hw_params(struct snd_ |
| 28 | u32 slots = (channels == 1) ? 2 : channels; |
| 29 | u32 slot_width = word_width; |
| 30 | u32 pins, bclk; |
| 31 | - int ret; |
| 32 | - int i; |
| 33 | - int trce_mask = 0; |
| 34 | + int ret, i, trce_mask = 0, dl_cfg_cnt, dl_cfg_idx = 0; |
| 35 | + struct fsl_sai_dl_cfg *dl_cfg; |
| 36 | |
| 37 | if (sai->slots) |
| 38 | slots = sai->slots; |
| 39 | |
| 40 | pins = DIV_ROUND_UP(channels, slots); |
| 41 | sai->is_dsd = fsl_is_dsd(params); |
| 42 | - if (sai->is_dsd) |
| 43 | + if (sai->is_dsd) { |
| 44 | pins = channels; |
| 45 | + dl_cfg = sai->dsd_dl_cfg; |
| 46 | + dl_cfg_cnt = sai->dsd_dl_cfg_cnt; |
| 47 | + } else { |
| 48 | + dl_cfg = sai->pcm_dl_cfg; |
| 49 | + dl_cfg_cnt = sai->pcm_dl_cfg_cnt; |
| 50 | + } |
| 51 | + |
| 52 | + for (i = 0; i < dl_cfg_cnt; i++) { |
| 53 | + if (dl_cfg[i].pins == pins) { |
| 54 | + dl_cfg_idx = i; |
| 55 | + break; |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + if (dl_cfg_idx >= dl_cfg_cnt) { |
| 60 | + dev_err(cpu_dai->dev, "fsl,dataline%s invalid or not provided.\n", |
| 61 | + sai->is_dsd ? ",dsd" : ""); |
| 62 | + return -EINVAL; |
| 63 | + } |
| 64 | |
| 65 | if (sai->slot_width) |
| 66 | slot_width = sai->slot_width; |
| 67 | @@ -713,7 +731,7 @@ static int fsl_sai_hw_params(struct snd_ |
| 68 | |
| 69 | if (sai->soc->dataline != 0x1) { |
| 70 | |
| 71 | - if (sai->dataline[tx] <= 1 || sai->is_multi_lane) |
| 72 | + if (dl_cfg[dl_cfg_idx].mask[tx] <= 1 || sai->is_multi_lane) |
| 73 | regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, offset), |
| 74 | FSL_SAI_CR4_FCOMB_MASK, 0); |
| 75 | else |
| 76 | @@ -724,21 +742,13 @@ static int fsl_sai_hw_params(struct snd_ |
| 77 | if (tx) { |
| 78 | sai->dma_params_tx.maxburst = |
| 79 | FSL_SAI_MAXBURST_TX * pins; |
| 80 | - if (sai->is_dsd) |
| 81 | - sai->dma_params_tx.fifo_num = pins + |
| 82 | - (sai->dataline_off_dsd[tx] << 4); |
| 83 | - else |
| 84 | - sai->dma_params_tx.fifo_num = pins + |
| 85 | - (sai->dataline_off[tx] << 4); |
| 86 | + sai->dma_params_tx.fifo_num = pins + |
| 87 | + (dl_cfg[dl_cfg_idx].offset[tx] << 4); |
| 88 | } else { |
| 89 | sai->dma_params_rx.maxburst = |
| 90 | FSL_SAI_MAXBURST_RX * pins; |
| 91 | - if (sai->is_dsd) |
| 92 | - sai->dma_params_rx.fifo_num = pins + |
| 93 | - (sai->dataline_off_dsd[tx] << 4); |
| 94 | - else |
| 95 | - sai->dma_params_rx.fifo_num = pins + |
| 96 | - (sai->dataline_off[tx] << 4); |
| 97 | + sai->dma_params_rx.fifo_num = pins + |
| 98 | + (dl_cfg[dl_cfg_idx].offset[tx] << 4); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | @@ -746,38 +756,22 @@ static int fsl_sai_hw_params(struct snd_ |
| 103 | &sai->dma_params_rx); |
| 104 | } |
| 105 | |
| 106 | - if (sai->is_dsd) { |
| 107 | - if (__sw_hweight8(sai->dataline_dsd[tx] & 0xFF) < pins) { |
| 108 | - dev_err(cpu_dai->dev, "channel not supported\n"); |
| 109 | - return -EINVAL; |
| 110 | - } |
| 111 | - /*find a proper tcre setting*/ |
| 112 | - for (i = 0; i < 8; i++) { |
| 113 | - trce_mask = (1 << (i + 1)) - 1; |
| 114 | - if (__sw_hweight8(sai->dataline_dsd[tx] & trce_mask) == pins) |
| 115 | - break; |
| 116 | - } |
| 117 | - |
| 118 | - regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset), |
| 119 | - FSL_SAI_CR3_TRCE_MASK, |
| 120 | - FSL_SAI_CR3_TRCE((sai->dataline_dsd[tx] & trce_mask))); |
| 121 | - } else { |
| 122 | - if (__sw_hweight8(sai->dataline[tx] & 0xFF) < pins) { |
| 123 | - dev_err(cpu_dai->dev, "channel not supported\n"); |
| 124 | - return -EINVAL; |
| 125 | - } |
| 126 | - /*find a proper tcre setting*/ |
| 127 | - for (i = 0; i < 8; i++) { |
| 128 | - trce_mask = (1 << (i + 1)) - 1; |
| 129 | - if (__sw_hweight8(sai->dataline[tx] & trce_mask) == pins) |
| 130 | - break; |
| 131 | - } |
| 132 | + if (__sw_hweight8(dl_cfg[dl_cfg_idx].mask[tx] & 0xFF) < pins) { |
| 133 | + dev_err(cpu_dai->dev, "channel not supported\n"); |
| 134 | + return -EINVAL; |
| 135 | + } |
| 136 | |
| 137 | - regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset), |
| 138 | - FSL_SAI_CR3_TRCE_MASK, |
| 139 | - FSL_SAI_CR3_TRCE((sai->dataline[tx] & trce_mask))); |
| 140 | + /*find a proper tcre setting*/ |
| 141 | + for (i = 0; i < 8; i++) { |
| 142 | + trce_mask = (1 << (i + 1)) - 1; |
| 143 | + if (__sw_hweight8(dl_cfg[dl_cfg_idx].mask[tx] & trce_mask) == pins) |
| 144 | + break; |
| 145 | } |
| 146 | |
| 147 | + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset), |
| 148 | + FSL_SAI_CR3_TRCE_MASK, |
| 149 | + FSL_SAI_CR3_TRCE((dl_cfg[dl_cfg_idx].mask[tx] & trce_mask))); |
| 150 | + |
| 151 | regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, offset), |
| 152 | FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | |
| 153 | FSL_SAI_CR4_CHMOD_MASK, |
| 154 | @@ -820,15 +814,32 @@ static int fsl_sai_trigger(struct snd_pc |
| 155 | u32 slots = (channels == 1) ? 2 : channels; |
| 156 | u32 xcsr, count = 100; |
| 157 | u32 pins; |
| 158 | - int i = 0, j = 0, k = 0; |
| 159 | + int i = 0, j = 0, k = 0, dl_cfg_cnt, dl_cfg_idx = 0; |
| 160 | + struct fsl_sai_dl_cfg *dl_cfg; |
| 161 | |
| 162 | if (sai->slots) |
| 163 | slots = sai->slots; |
| 164 | |
| 165 | pins = DIV_ROUND_UP(channels, slots); |
| 166 | |
| 167 | - if (sai->is_dsd) |
| 168 | + if (sai->is_dsd) { |
| 169 | pins = channels; |
| 170 | + dl_cfg = sai->dsd_dl_cfg; |
| 171 | + dl_cfg_cnt = sai->dsd_dl_cfg_cnt; |
| 172 | + } else { |
| 173 | + dl_cfg = sai->pcm_dl_cfg; |
| 174 | + dl_cfg_cnt = sai->pcm_dl_cfg_cnt; |
| 175 | + } |
| 176 | + |
| 177 | + for (i = 0; i < dl_cfg_cnt; i++) { |
| 178 | + if (dl_cfg[i].pins == pins) { |
| 179 | + dl_cfg_idx = i; |
| 180 | + break; |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + i = 0; |
| 185 | + |
| 186 | /* |
| 187 | * Asynchronous mode: Clear SYNC for both Tx and Rx. |
| 188 | * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. |
| 189 | @@ -849,7 +860,7 @@ static int fsl_sai_trigger(struct snd_pc |
| 190 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| 191 | |
| 192 | while (tx && i < channels) { |
| 193 | - if ((sai->is_dsd ? sai->dataline_dsd[tx] : sai->dataline[tx]) & (1 << j)) { |
| 194 | + if (dl_cfg[dl_cfg_idx].mask[tx] & (1 << j)) { |
| 195 | regmap_write(sai->regmap, FSL_SAI_TDR0 + j * 0x4, 0x0); |
| 196 | i++; |
| 197 | k++; |
| 198 | @@ -1262,6 +1273,77 @@ static const struct of_device_id fsl_sai |
| 199 | }; |
| 200 | MODULE_DEVICE_TABLE(of, fsl_sai_ids); |
| 201 | |
| 202 | +static unsigned int fsl_sai_calc_dl_off(unsigned int* dl_mask) |
| 203 | +{ |
| 204 | + int fbidx, nbidx, offset; |
| 205 | + |
| 206 | + fbidx = find_first_bit((const unsigned long *)dl_mask, 8); |
| 207 | + nbidx = find_next_bit((const unsigned long *)dl_mask, 8, fbidx+1); |
| 208 | + offset = nbidx - fbidx - 1; |
| 209 | + |
| 210 | + return (offset < 0 || offset >= 7 ? 0 : offset); |
| 211 | +} |
| 212 | + |
| 213 | +static int fsl_sai_read_dlcfg(struct platform_device *pdev, char *pn, |
| 214 | + struct fsl_sai_dl_cfg **rcfg, unsigned int soc_dl) |
| 215 | +{ |
| 216 | + int ret, elems, i, index, num_cfg; |
| 217 | + struct device_node *np = pdev->dev.of_node; |
| 218 | + struct fsl_sai_dl_cfg *cfg; |
| 219 | + u32 rx, tx, pins; |
| 220 | + |
| 221 | + *rcfg = NULL; |
| 222 | + |
| 223 | + elems = of_property_count_u32_elems(np, pn); |
| 224 | + |
| 225 | + /* consider default value "0 0x1 0x1" if property is missing */ |
| 226 | + if (elems <= 0) |
| 227 | + elems = 3; |
| 228 | + |
| 229 | + if (elems % 3) { |
| 230 | + dev_err(&pdev->dev, |
| 231 | + "Number of elements in %s must be divisible to 3.\n", pn); |
| 232 | + return -EINVAL; |
| 233 | + } |
| 234 | + |
| 235 | + num_cfg = elems / 3; |
| 236 | + cfg = devm_kzalloc(&pdev->dev, num_cfg * sizeof(*cfg), GFP_KERNEL); |
| 237 | + if (cfg == NULL) { |
| 238 | + dev_err(&pdev->dev, "Cannot allocate memory for %s.\n", pn); |
| 239 | + return -ENOMEM; |
| 240 | + } |
| 241 | + |
| 242 | + for (i = 0, index = 0; i < num_cfg; i++) { |
| 243 | + ret = of_property_read_u32_index(np, pn, index++, &pins); |
| 244 | + if (ret) |
| 245 | + pins = 0; |
| 246 | + |
| 247 | + ret = of_property_read_u32_index(np, pn, index++, &rx); |
| 248 | + if (ret) |
| 249 | + rx = 1; |
| 250 | + |
| 251 | + ret = of_property_read_u32_index(np, pn, index++, &tx); |
| 252 | + if (ret) |
| 253 | + tx = 1; |
| 254 | + |
| 255 | + if ((rx & ~soc_dl) || (tx & ~soc_dl)) { |
| 256 | + dev_err(&pdev->dev, |
| 257 | + "%s: dataline cfg[%d] setting error, mask is 0x%x\n", |
| 258 | + pn, i, soc_dl); |
| 259 | + return -EINVAL; |
| 260 | + } |
| 261 | + |
| 262 | + cfg[i].pins = pins; |
| 263 | + cfg[i].mask[0] = rx; |
| 264 | + cfg[i].offset[0] = fsl_sai_calc_dl_off(&rx); |
| 265 | + cfg[i].mask[1] = tx; |
| 266 | + cfg[i].offset[1] = fsl_sai_calc_dl_off(&tx); |
| 267 | + } |
| 268 | + |
| 269 | + *rcfg = cfg; |
| 270 | + return num_cfg; |
| 271 | +} |
| 272 | + |
| 273 | static int fsl_sai_probe(struct platform_device *pdev) |
| 274 | { |
| 275 | struct device_node *np = pdev->dev.of_node; |
| 276 | @@ -1273,7 +1355,6 @@ static int fsl_sai_probe(struct platform |
| 277 | char tmp[8]; |
| 278 | int irq, ret, i; |
| 279 | int index; |
| 280 | - int firstbitidx, nextbitidx, offset; |
| 281 | struct regmap_config fsl_sai_regmap_config = fsl_sai_v2_regmap_config; |
| 282 | unsigned long irqflags = 0; |
| 283 | |
| 284 | @@ -1340,45 +1421,19 @@ static int fsl_sai_probe(struct platform |
| 285 | sai->is_multi_lane = true; |
| 286 | |
| 287 | /*dataline mask for rx and tx*/ |
| 288 | - ret = of_property_read_u32_index(np, "fsl,dataline", 0, &sai->dataline[0]); |
| 289 | - if (ret) |
| 290 | - sai->dataline[0] = 1; |
| 291 | - |
| 292 | - ret = of_property_read_u32_index(np, "fsl,dataline", 1, &sai->dataline[1]); |
| 293 | - if (ret) |
| 294 | - sai->dataline[1] = 1; |
| 295 | - |
| 296 | - if ((sai->dataline[0] & (~sai->soc->dataline)) || sai->dataline[1] & (~sai->soc->dataline)) { |
| 297 | - dev_err(&pdev->dev, "dataline setting error, Mask is 0x%x\n", sai->soc->dataline); |
| 298 | - return -EINVAL; |
| 299 | - } |
| 300 | - |
| 301 | - for (i = 0; i < 2; i++) { |
| 302 | - firstbitidx = find_first_bit((const unsigned long *)&sai->dataline[i], 8); |
| 303 | - nextbitidx = find_next_bit((const unsigned long *)&sai->dataline[i], 8, firstbitidx+1); |
| 304 | - offset = nextbitidx - firstbitidx - 1; |
| 305 | - sai->dataline_off[i] = (offset < 0 || offset >= 7 ? 0 : offset); |
| 306 | - } |
| 307 | - |
| 308 | - ret = of_property_read_u32_index(np, "fsl,dataline,dsd", 0, &sai->dataline_dsd[0]); |
| 309 | - if (ret) |
| 310 | - sai->dataline_dsd[0] = 1; |
| 311 | - |
| 312 | - ret = of_property_read_u32_index(np, "fsl,dataline,dsd", 1, &sai->dataline_dsd[1]); |
| 313 | - if (ret) |
| 314 | - sai->dataline_dsd[1] = 1; |
| 315 | + ret = fsl_sai_read_dlcfg(pdev, "fsl,dataline", &sai->pcm_dl_cfg, |
| 316 | + sai->soc->dataline); |
| 317 | + if (ret < 0) |
| 318 | + return ret; |
| 319 | + |
| 320 | + sai->pcm_dl_cfg_cnt = ret; |
| 321 | + |
| 322 | + ret = fsl_sai_read_dlcfg(pdev, "fsl,dataline,dsd", &sai->dsd_dl_cfg, |
| 323 | + sai->soc->dataline); |
| 324 | + if (ret < 0) |
| 325 | + return ret; |
| 326 | |
| 327 | - if ((sai->dataline_dsd[0] & (~sai->soc->dataline)) || sai->dataline_dsd[1] & (~sai->soc->dataline)) { |
| 328 | - dev_err(&pdev->dev, "dataline setting error, Mask is 0x%x\n", sai->soc->dataline); |
| 329 | - return -EINVAL; |
| 330 | - } |
| 331 | - |
| 332 | - for (i = 0; i < 2; i++) { |
| 333 | - firstbitidx = find_first_bit((const unsigned long *)&sai->dataline_dsd[i], 8); |
| 334 | - nextbitidx = find_next_bit((const unsigned long *)&sai->dataline_dsd[i], 8, firstbitidx+1); |
| 335 | - offset = nextbitidx - firstbitidx - 1; |
| 336 | - sai->dataline_off_dsd[i] = (offset < 0 || offset >= 7 ? 0 : offset); |
| 337 | - } |
| 338 | + sai->dsd_dl_cfg_cnt = ret; |
| 339 | |
| 340 | if ((of_find_property(np, "fsl,i2s-xtor", NULL) != NULL) || |
| 341 | (of_find_property(np, "fsl,txm-rxs", NULL) != NULL)) |
| 342 | --- a/sound/soc/fsl/fsl_sai.h |
| 343 | +++ b/sound/soc/fsl/fsl_sai.h |
| 344 | @@ -234,6 +234,12 @@ struct fsl_sai_param { |
| 345 | u32 dln; /* number of datalines implemented */ |
| 346 | }; |
| 347 | |
| 348 | +struct fsl_sai_dl_cfg { |
| 349 | + unsigned int pins; |
| 350 | + unsigned int mask[2]; |
| 351 | + unsigned int offset[2]; |
| 352 | +}; |
| 353 | + |
| 354 | struct fsl_sai { |
| 355 | struct platform_device *pdev; |
| 356 | struct regmap *regmap; |
| 357 | @@ -249,10 +255,12 @@ struct fsl_sai { |
| 358 | bool synchronous[2]; |
| 359 | bool is_stream_opened[2]; |
| 360 | bool is_dsd; |
| 361 | - unsigned int dataline[2]; |
| 362 | - unsigned int dataline_dsd[2]; |
| 363 | - unsigned int dataline_off[2]; |
| 364 | - unsigned int dataline_off_dsd[2]; |
| 365 | + |
| 366 | + int pcm_dl_cfg_cnt; |
| 367 | + int dsd_dl_cfg_cnt; |
| 368 | + struct fsl_sai_dl_cfg *pcm_dl_cfg; |
| 369 | + struct fsl_sai_dl_cfg *dsd_dl_cfg; |
| 370 | + |
| 371 | unsigned int masterflag[2]; |
| 372 | |
| 373 | unsigned int mclk_id[2]; |