1fce21744STroy Mitchell // SPDX-License-Identifier: GPL-2.0 2fce21744STroy Mitchell /* Copyright (c) 2025 Troy Mitchell <troy.mitchell@linux.spacemit.com> */ 3fce21744STroy Mitchell 4fce21744STroy Mitchell #include <linux/bitfield.h> 5fce21744STroy Mitchell #include <linux/clk.h> 6fce21744STroy Mitchell #include <linux/reset.h> 7fce21744STroy Mitchell #include <sound/dmaengine_pcm.h> 8fce21744STroy Mitchell #include <sound/pcm.h> 9fce21744STroy Mitchell #include <sound/pcm_params.h> 10fce21744STroy Mitchell 11fce21744STroy Mitchell #define SSCR 0x00 /* SPI/I2S top control register */ 12fce21744STroy Mitchell #define SSFCR 0x04 /* SPI/I2S FIFO control register */ 13fce21744STroy Mitchell #define SSINTEN 0x08 /* SPI/I2S interrupt enable register */ 14fce21744STroy Mitchell #define SSDATR 0x10 /* SPI/I2S data register */ 15fce21744STroy Mitchell #define SSPSP 0x18 /* SPI/I2S programmable serial protocol control register */ 16fce21744STroy Mitchell #define SSRWT 0x24 /* SPI/I2S root control register */ 17fce21744STroy Mitchell 18fce21744STroy Mitchell /* SPI/I2S Work data size, register bits value 0~31 indicated data size 1~32 bits */ 19fce21744STroy Mitchell #define SSCR_FIELD_DSS GENMASK(9, 5) 20fce21744STroy Mitchell #define SSCR_DW_8BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x7) 21fce21744STroy Mitchell #define SSCR_DW_16BYTE FIELD_PREP(SSCR_FIELD_DSS, 0xf) 22fce21744STroy Mitchell #define SSCR_DW_18BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x11) 23fce21744STroy Mitchell #define SSCR_DW_32BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x1f) 24fce21744STroy Mitchell 25fce21744STroy Mitchell #define SSCR_SSE BIT(0) /* SPI/I2S Enable */ 26fce21744STroy Mitchell #define SSCR_FRF_PSP GENMASK(2, 1) /* Frame Format*/ 27fce21744STroy Mitchell #define SSCR_TRAIL BIT(13) /* Trailing Byte */ 28fce21744STroy Mitchell 29fce21744STroy Mitchell #define SSFCR_FIELD_TFT GENMASK(3, 0) /* TXFIFO Trigger Threshold */ 30fce21744STroy Mitchell #define SSFCR_FIELD_RFT GENMASK(8, 5) /* RXFIFO Trigger Threshold */ 31fce21744STroy Mitchell #define SSFCR_TSRE BIT(10) /* Transmit Service Request Enable */ 32fce21744STroy Mitchell #define SSFCR_RSRE BIT(11) /* Receive Service Request Enable */ 33fce21744STroy Mitchell 34fce21744STroy Mitchell #define SSPSP_FSRT BIT(3) /* Frame Sync Relative Timing Bit */ 35fce21744STroy Mitchell #define SSPSP_SFRMP BIT(4) /* Serial Frame Polarity */ 36fce21744STroy Mitchell #define SSPSP_FIELD_SFRMWDTH GENMASK(17, 12) /* Serial Frame Width field */ 37fce21744STroy Mitchell 38fce21744STroy Mitchell #define SSRWT_RWOT BIT(0) /* Receive Without Transmit */ 39fce21744STroy Mitchell 40fce21744STroy Mitchell #define SPACEMIT_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ 41fce21744STroy Mitchell SNDRV_PCM_RATE_48000) 42fce21744STroy Mitchell #define SPACEMIT_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) 43fce21744STroy Mitchell 44fce21744STroy Mitchell #define SPACEMIT_I2S_PERIOD_SIZE 1024 45fce21744STroy Mitchell 46fce21744STroy Mitchell struct spacemit_i2s_dev { 47fce21744STroy Mitchell struct device *dev; 48fce21744STroy Mitchell 49fce21744STroy Mitchell void __iomem *base; 50fce21744STroy Mitchell 51fce21744STroy Mitchell struct reset_control *reset; 52fce21744STroy Mitchell 53fce21744STroy Mitchell struct clk *sysclk; 54fce21744STroy Mitchell struct clk *bclk; 55fce21744STroy Mitchell struct clk *sspa_clk; 56fce21744STroy Mitchell 57fce21744STroy Mitchell struct snd_dmaengine_dai_dma_data capture_dma_data; 58fce21744STroy Mitchell struct snd_dmaengine_dai_dma_data playback_dma_data; 59fce21744STroy Mitchell 60fce21744STroy Mitchell bool has_capture; 61fce21744STroy Mitchell bool has_playback; 62fce21744STroy Mitchell 63fce21744STroy Mitchell int dai_fmt; 64fce21744STroy Mitchell 65fce21744STroy Mitchell int started_count; 66fce21744STroy Mitchell }; 67fce21744STroy Mitchell 68fce21744STroy Mitchell static const struct snd_pcm_hardware spacemit_pcm_hardware = { 69fce21744STroy Mitchell .info = SNDRV_PCM_INFO_INTERLEAVED | 70fce21744STroy Mitchell SNDRV_PCM_INFO_BATCH, 71fce21744STroy Mitchell .formats = SPACEMIT_PCM_FORMATS, 72fce21744STroy Mitchell .rates = SPACEMIT_PCM_RATES, 73fce21744STroy Mitchell .rate_min = SNDRV_PCM_RATE_8000, 74fce21744STroy Mitchell .rate_max = SNDRV_PCM_RATE_192000, 75fce21744STroy Mitchell .channels_min = 1, 76fce21744STroy Mitchell .channels_max = 2, 77fce21744STroy Mitchell .buffer_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4 * 4, 78fce21744STroy Mitchell .period_bytes_min = SPACEMIT_I2S_PERIOD_SIZE * 2, 79fce21744STroy Mitchell .period_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4, 80fce21744STroy Mitchell .periods_min = 2, 81fce21744STroy Mitchell .periods_max = 4, 82fce21744STroy Mitchell }; 83fce21744STroy Mitchell 84fce21744STroy Mitchell static const struct snd_dmaengine_pcm_config spacemit_dmaengine_pcm_config = { 85fce21744STroy Mitchell .pcm_hardware = &spacemit_pcm_hardware, 86fce21744STroy Mitchell .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 87fce21744STroy Mitchell .chan_names = {"tx", "rx"}, 88fce21744STroy Mitchell .prealloc_buffer_size = 32 * 1024, 89fce21744STroy Mitchell }; 90fce21744STroy Mitchell 91fce21744STroy Mitchell static void spacemit_i2s_init(struct spacemit_i2s_dev *i2s) 92fce21744STroy Mitchell { 93fce21744STroy Mitchell u32 sscr_val, sspsp_val, ssfcr_val, ssrwt_val; 94fce21744STroy Mitchell 95fce21744STroy Mitchell sscr_val = SSCR_TRAIL | SSCR_FRF_PSP; 96fce21744STroy Mitchell ssfcr_val = FIELD_PREP(SSFCR_FIELD_TFT, 5) | 97fce21744STroy Mitchell FIELD_PREP(SSFCR_FIELD_RFT, 5) | 98fce21744STroy Mitchell SSFCR_RSRE | SSFCR_TSRE; 99fce21744STroy Mitchell ssrwt_val = SSRWT_RWOT; 100fce21744STroy Mitchell sspsp_val = SSPSP_SFRMP; 101fce21744STroy Mitchell 102fce21744STroy Mitchell writel(sscr_val, i2s->base + SSCR); 103fce21744STroy Mitchell writel(ssfcr_val, i2s->base + SSFCR); 104fce21744STroy Mitchell writel(sspsp_val, i2s->base + SSPSP); 105fce21744STroy Mitchell writel(ssrwt_val, i2s->base + SSRWT); 106fce21744STroy Mitchell writel(0, i2s->base + SSINTEN); 107fce21744STroy Mitchell } 108fce21744STroy Mitchell 109fce21744STroy Mitchell static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, 110fce21744STroy Mitchell struct snd_pcm_hw_params *params, 111fce21744STroy Mitchell struct snd_soc_dai *dai) 112fce21744STroy Mitchell { 113fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); 114fce21744STroy Mitchell struct snd_dmaengine_dai_dma_data *dma_data; 115fce21744STroy Mitchell u32 data_width, data_bits; 116fce21744STroy Mitchell unsigned long bclk_rate; 117fce21744STroy Mitchell u32 val; 118fce21744STroy Mitchell int ret; 119fce21744STroy Mitchell 120fce21744STroy Mitchell val = readl(i2s->base + SSCR); 121fce21744STroy Mitchell if (val & SSCR_SSE) 122fce21744STroy Mitchell return 0; 123fce21744STroy Mitchell 124fce21744STroy Mitchell dma_data = &i2s->playback_dma_data; 125fce21744STroy Mitchell 126fce21744STroy Mitchell if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 127fce21744STroy Mitchell dma_data = &i2s->capture_dma_data; 128fce21744STroy Mitchell 129fce21744STroy Mitchell switch (params_format(params)) { 130fce21744STroy Mitchell case SNDRV_PCM_FORMAT_S8: 131fce21744STroy Mitchell data_bits = 8; 132fce21744STroy Mitchell data_width = SSCR_DW_8BYTE; 133fce21744STroy Mitchell dma_data->maxburst = 8; 134fce21744STroy Mitchell dma_data->addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 135fce21744STroy Mitchell break; 136fce21744STroy Mitchell case SNDRV_PCM_FORMAT_S16_LE: 137fce21744STroy Mitchell data_bits = 16; 138fce21744STroy Mitchell data_width = SSCR_DW_16BYTE; 139fce21744STroy Mitchell dma_data->maxburst = 16; 140fce21744STroy Mitchell dma_data->addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 141fce21744STroy Mitchell break; 142fce21744STroy Mitchell case SNDRV_PCM_FORMAT_S32_LE: 143fce21744STroy Mitchell data_bits = 32; 144fce21744STroy Mitchell data_width = SSCR_DW_32BYTE; 145fce21744STroy Mitchell dma_data->maxburst = 32; 146fce21744STroy Mitchell dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 147fce21744STroy Mitchell break; 148fce21744STroy Mitchell default: 149fce21744STroy Mitchell dev_dbg(i2s->dev, "unexpected data width type"); 150fce21744STroy Mitchell return -EINVAL; 151fce21744STroy Mitchell } 152fce21744STroy Mitchell 153fce21744STroy Mitchell switch (i2s->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 154fce21744STroy Mitchell case SND_SOC_DAIFMT_I2S: 155fce21744STroy Mitchell if (data_bits == 16) { 156fce21744STroy Mitchell data_width = SSCR_DW_32BYTE; 157fce21744STroy Mitchell dma_data->maxburst = 32; 158fce21744STroy Mitchell dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 159fce21744STroy Mitchell } 160fce21744STroy Mitchell 161fce21744STroy Mitchell snd_pcm_hw_constraint_minmax(substream->runtime, 162fce21744STroy Mitchell SNDRV_PCM_HW_PARAM_CHANNELS, 163fce21744STroy Mitchell 1, 2); 164fce21744STroy Mitchell snd_pcm_hw_constraint_mask64(substream->runtime, 165fce21744STroy Mitchell SNDRV_PCM_HW_PARAM_FORMAT, 166fce21744STroy Mitchell SNDRV_PCM_FMTBIT_S16_LE); 167fce21744STroy Mitchell break; 168fce21744STroy Mitchell case SND_SOC_DAIFMT_DSP_A: 169fce21744STroy Mitchell case SND_SOC_DAIFMT_DSP_B: 170fce21744STroy Mitchell snd_pcm_hw_constraint_minmax(substream->runtime, 171fce21744STroy Mitchell SNDRV_PCM_HW_PARAM_CHANNELS, 172fce21744STroy Mitchell 1, 1); 173fce21744STroy Mitchell snd_pcm_hw_constraint_mask64(substream->runtime, 174fce21744STroy Mitchell SNDRV_PCM_HW_PARAM_FORMAT, 175fce21744STroy Mitchell SNDRV_PCM_FMTBIT_S32_LE); 176fce21744STroy Mitchell break; 177fce21744STroy Mitchell default: 178fce21744STroy Mitchell dev_dbg(i2s->dev, "unexpected format type"); 179fce21744STroy Mitchell return -EINVAL; 180fce21744STroy Mitchell 181fce21744STroy Mitchell } 182fce21744STroy Mitchell 183fce21744STroy Mitchell val = readl(i2s->base + SSCR); 184fce21744STroy Mitchell val &= ~SSCR_DW_32BYTE; 185fce21744STroy Mitchell val |= data_width; 186fce21744STroy Mitchell writel(val, i2s->base + SSCR); 187fce21744STroy Mitchell 188fce21744STroy Mitchell bclk_rate = params_channels(params) * 189fce21744STroy Mitchell params_rate(params) * 190fce21744STroy Mitchell data_bits; 191fce21744STroy Mitchell 192fce21744STroy Mitchell ret = clk_set_rate(i2s->bclk, bclk_rate); 193fce21744STroy Mitchell if (ret) 194fce21744STroy Mitchell return ret; 195fce21744STroy Mitchell 196fce21744STroy Mitchell return clk_set_rate(i2s->sspa_clk, bclk_rate); 197fce21744STroy Mitchell } 198fce21744STroy Mitchell 199fce21744STroy Mitchell static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, 200fce21744STroy Mitchell unsigned int freq, int dir) 201fce21744STroy Mitchell { 202fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev); 203fce21744STroy Mitchell 204fce21744STroy Mitchell if (freq == 0) 205fce21744STroy Mitchell return 0; 206fce21744STroy Mitchell 207fce21744STroy Mitchell return clk_set_rate(i2s->sysclk, freq); 208fce21744STroy Mitchell } 209fce21744STroy Mitchell 210fce21744STroy Mitchell static int spacemit_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 211fce21744STroy Mitchell unsigned int fmt) 212fce21744STroy Mitchell { 213fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev); 214fce21744STroy Mitchell u32 sspsp_val; 215fce21744STroy Mitchell 216fce21744STroy Mitchell sspsp_val = readl(i2s->base + SSPSP); 217fce21744STroy Mitchell sspsp_val &= ~SSPSP_FIELD_SFRMWDTH; 218fce21744STroy Mitchell sspsp_val |= SSPSP_FSRT; 219fce21744STroy Mitchell 220fce21744STroy Mitchell i2s->dai_fmt = fmt; 221fce21744STroy Mitchell 222fce21744STroy Mitchell switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 223fce21744STroy Mitchell case SND_SOC_DAIFMT_I2S: 224fce21744STroy Mitchell sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x10); 225fce21744STroy Mitchell break; 226fce21744STroy Mitchell case SND_SOC_DAIFMT_DSP_B: 227fce21744STroy Mitchell /* DSP_B: next frame asserted after previous frame end, so clear FSRT */ 228fce21744STroy Mitchell sspsp_val &= ~SSPSP_FSRT; 229fce21744STroy Mitchell fallthrough; 230fce21744STroy Mitchell case SND_SOC_DAIFMT_DSP_A: 231fce21744STroy Mitchell sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x1); 232fce21744STroy Mitchell break; 233fce21744STroy Mitchell default: 234fce21744STroy Mitchell dev_dbg(i2s->dev, "unexpected format type"); 235fce21744STroy Mitchell return -EINVAL; 236fce21744STroy Mitchell } 237fce21744STroy Mitchell 238fce21744STroy Mitchell writel(sspsp_val, i2s->base + SSPSP); 239fce21744STroy Mitchell 240fce21744STroy Mitchell return 0; 241fce21744STroy Mitchell } 242fce21744STroy Mitchell 243fce21744STroy Mitchell static int spacemit_i2s_trigger(struct snd_pcm_substream *substream, 244fce21744STroy Mitchell int cmd, struct snd_soc_dai *dai) 245fce21744STroy Mitchell { 246fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); 247fce21744STroy Mitchell u32 val; 248fce21744STroy Mitchell 249fce21744STroy Mitchell switch (cmd) { 250fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_START: 251fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_RESUME: 252fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 253fce21744STroy Mitchell if (!i2s->started_count) { 254fce21744STroy Mitchell val = readl(i2s->base + SSCR); 255fce21744STroy Mitchell val |= SSCR_SSE; 256fce21744STroy Mitchell writel(val, i2s->base + SSCR); 257fce21744STroy Mitchell } 258fce21744STroy Mitchell i2s->started_count++; 259fce21744STroy Mitchell break; 260fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_STOP: 261fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_SUSPEND: 262fce21744STroy Mitchell case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 263fce21744STroy Mitchell if (i2s->started_count) 264fce21744STroy Mitchell i2s->started_count--; 265fce21744STroy Mitchell 266fce21744STroy Mitchell if (!i2s->started_count) { 267fce21744STroy Mitchell val = readl(i2s->base + SSCR); 268fce21744STroy Mitchell val &= ~SSCR_SSE; 269fce21744STroy Mitchell writel(val, i2s->base + SSCR); 270fce21744STroy Mitchell } 271fce21744STroy Mitchell break; 272fce21744STroy Mitchell default: 273fce21744STroy Mitchell return -EINVAL; 274fce21744STroy Mitchell } 275fce21744STroy Mitchell 276fce21744STroy Mitchell return 0; 277fce21744STroy Mitchell } 278fce21744STroy Mitchell 279fce21744STroy Mitchell static int spacemit_i2s_dai_probe(struct snd_soc_dai *dai) 280fce21744STroy Mitchell { 281fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); 282fce21744STroy Mitchell 283fce21744STroy Mitchell snd_soc_dai_init_dma_data(dai, 284fce21744STroy Mitchell i2s->has_playback ? &i2s->playback_dma_data : NULL, 285fce21744STroy Mitchell i2s->has_capture ? &i2s->capture_dma_data : NULL); 286fce21744STroy Mitchell 287fce21744STroy Mitchell reset_control_deassert(i2s->reset); 288fce21744STroy Mitchell 289fce21744STroy Mitchell spacemit_i2s_init(i2s); 290fce21744STroy Mitchell 291fce21744STroy Mitchell return 0; 292fce21744STroy Mitchell } 293fce21744STroy Mitchell 294fce21744STroy Mitchell static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) 295fce21744STroy Mitchell { 296fce21744STroy Mitchell struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); 297fce21744STroy Mitchell 298fce21744STroy Mitchell reset_control_assert(i2s->reset); 299fce21744STroy Mitchell 300fce21744STroy Mitchell return 0; 301fce21744STroy Mitchell } 302fce21744STroy Mitchell 303fce21744STroy Mitchell static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { 304fce21744STroy Mitchell .probe = spacemit_i2s_dai_probe, 305fce21744STroy Mitchell .remove = spacemit_i2s_dai_remove, 306fce21744STroy Mitchell .hw_params = spacemit_i2s_hw_params, 307fce21744STroy Mitchell .set_sysclk = spacemit_i2s_set_sysclk, 308fce21744STroy Mitchell .set_fmt = spacemit_i2s_set_fmt, 309fce21744STroy Mitchell .trigger = spacemit_i2s_trigger, 310fce21744STroy Mitchell }; 311fce21744STroy Mitchell 312fce21744STroy Mitchell static struct snd_soc_dai_driver spacemit_i2s_dai = { 313fce21744STroy Mitchell .ops = &spacemit_i2s_dai_ops, 314fce21744STroy Mitchell .playback = { 315fce21744STroy Mitchell .channels_min = 1, 316fce21744STroy Mitchell .channels_max = 2, 317fce21744STroy Mitchell .rates = SPACEMIT_PCM_RATES, 318fce21744STroy Mitchell .rate_min = SNDRV_PCM_RATE_8000, 319fce21744STroy Mitchell .rate_max = SNDRV_PCM_RATE_48000, 320fce21744STroy Mitchell .formats = SPACEMIT_PCM_FORMATS, 321fce21744STroy Mitchell }, 322fce21744STroy Mitchell .capture = { 323fce21744STroy Mitchell .channels_min = 1, 324fce21744STroy Mitchell .channels_max = 2, 325fce21744STroy Mitchell .rates = SPACEMIT_PCM_RATES, 326fce21744STroy Mitchell .rate_min = SNDRV_PCM_RATE_8000, 327fce21744STroy Mitchell .rate_max = SNDRV_PCM_RATE_48000, 328fce21744STroy Mitchell .formats = SPACEMIT_PCM_FORMATS, 329fce21744STroy Mitchell }, 330fce21744STroy Mitchell .symmetric_rate = 1, 331fce21744STroy Mitchell }; 332fce21744STroy Mitchell 333fce21744STroy Mitchell static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, 334fce21744STroy Mitchell struct snd_soc_dai_driver **dp, 335fce21744STroy Mitchell dma_addr_t addr) 336fce21744STroy Mitchell { 337fce21744STroy Mitchell struct device_node *node = i2s->dev->of_node; 338fce21744STroy Mitchell struct snd_soc_dai_driver *dai; 339fce21744STroy Mitchell struct property *dma_names; 340fce21744STroy Mitchell const char *dma_name; 341fce21744STroy Mitchell 342fce21744STroy Mitchell of_property_for_each_string(node, "dma-names", dma_names, dma_name) { 343fce21744STroy Mitchell if (!strcmp(dma_name, "tx")) 344fce21744STroy Mitchell i2s->has_playback = true; 345fce21744STroy Mitchell if (!strcmp(dma_name, "rx")) 346fce21744STroy Mitchell i2s->has_capture = true; 347fce21744STroy Mitchell } 348fce21744STroy Mitchell 349fce21744STroy Mitchell dai = devm_kmemdup(i2s->dev, &spacemit_i2s_dai, 350fce21744STroy Mitchell sizeof(*dai), GFP_KERNEL); 351fce21744STroy Mitchell if (!dai) 352fce21744STroy Mitchell return -ENOMEM; 353fce21744STroy Mitchell 354fce21744STroy Mitchell if (i2s->has_playback) { 355fce21744STroy Mitchell dai->playback.stream_name = "Playback"; 356fce21744STroy Mitchell dai->playback.channels_min = 1; 357fce21744STroy Mitchell dai->playback.channels_max = 2; 358fce21744STroy Mitchell dai->playback.rates = SPACEMIT_PCM_RATES; 359fce21744STroy Mitchell dai->playback.formats = SPACEMIT_PCM_FORMATS; 360fce21744STroy Mitchell 361fce21744STroy Mitchell i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 362fce21744STroy Mitchell i2s->playback_dma_data.maxburst = 32; 363fce21744STroy Mitchell i2s->playback_dma_data.addr = addr; 364fce21744STroy Mitchell } 365fce21744STroy Mitchell 366fce21744STroy Mitchell if (i2s->has_capture) { 367fce21744STroy Mitchell dai->capture.stream_name = "Capture"; 368fce21744STroy Mitchell dai->capture.channels_min = 1; 369fce21744STroy Mitchell dai->capture.channels_max = 2; 370fce21744STroy Mitchell dai->capture.rates = SPACEMIT_PCM_RATES; 371fce21744STroy Mitchell dai->capture.formats = SPACEMIT_PCM_FORMATS; 372fce21744STroy Mitchell 373fce21744STroy Mitchell i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 374fce21744STroy Mitchell i2s->capture_dma_data.maxburst = 32; 375fce21744STroy Mitchell i2s->capture_dma_data.addr = addr; 376fce21744STroy Mitchell } 377fce21744STroy Mitchell 378fce21744STroy Mitchell if (dp) 379fce21744STroy Mitchell *dp = dai; 380fce21744STroy Mitchell 381fce21744STroy Mitchell return 0; 382fce21744STroy Mitchell } 383fce21744STroy Mitchell 384fce21744STroy Mitchell static const struct snd_soc_component_driver spacemit_i2s_component = { 385fce21744STroy Mitchell .name = "i2s-k1", 386fce21744STroy Mitchell .legacy_dai_naming = 1, 387fce21744STroy Mitchell }; 388fce21744STroy Mitchell 389fce21744STroy Mitchell static int spacemit_i2s_probe(struct platform_device *pdev) 390fce21744STroy Mitchell { 391fce21744STroy Mitchell struct snd_soc_dai_driver *dai; 392fce21744STroy Mitchell struct spacemit_i2s_dev *i2s; 393fce21744STroy Mitchell struct resource *res; 394fce21744STroy Mitchell struct clk *clk; 395fce21744STroy Mitchell int ret; 396fce21744STroy Mitchell 397fce21744STroy Mitchell i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); 398fce21744STroy Mitchell if (!i2s) 399fce21744STroy Mitchell return -ENOMEM; 400fce21744STroy Mitchell 401fce21744STroy Mitchell i2s->dev = &pdev->dev; 402fce21744STroy Mitchell 403fce21744STroy Mitchell i2s->sysclk = devm_clk_get_enabled(i2s->dev, "sysclk"); 404fce21744STroy Mitchell if (IS_ERR(i2s->sysclk)) 405fce21744STroy Mitchell return dev_err_probe(i2s->dev, PTR_ERR(i2s->sysclk), 406fce21744STroy Mitchell "failed to enable sysbase clock\n"); 407fce21744STroy Mitchell 408fce21744STroy Mitchell i2s->bclk = devm_clk_get_enabled(i2s->dev, "bclk"); 409fce21744STroy Mitchell if (IS_ERR(i2s->bclk)) 410fce21744STroy Mitchell return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n"); 411fce21744STroy Mitchell 412fce21744STroy Mitchell clk = devm_clk_get_enabled(i2s->dev, "sspa_bus"); 413fce21744STroy Mitchell if (IS_ERR(clk)) 414fce21744STroy Mitchell return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n"); 415fce21744STroy Mitchell 416fce21744STroy Mitchell i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa"); 417*e32c4025SGoko Mell if (IS_ERR(i2s->sspa_clk)) 418*e32c4025SGoko Mell return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), 419*e32c4025SGoko Mell "failed to enable sspa clock\n"); 420fce21744STroy Mitchell 421fce21744STroy Mitchell i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 422fce21744STroy Mitchell if (IS_ERR(i2s->base)) 423fce21744STroy Mitchell return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); 424fce21744STroy Mitchell 425fce21744STroy Mitchell i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); 426fce21744STroy Mitchell if (IS_ERR(i2s->reset)) 427fce21744STroy Mitchell return dev_err_probe(i2s->dev, PTR_ERR(i2s->reset), 428fce21744STroy Mitchell "failed to get reset control"); 429fce21744STroy Mitchell 430fce21744STroy Mitchell dev_set_drvdata(i2s->dev, i2s); 431fce21744STroy Mitchell 432f034c16aSTroy Mitchell ret = spacemit_i2s_init_dai(i2s, &dai, res->start + SSDATR); 433f034c16aSTroy Mitchell if (ret) 434f034c16aSTroy Mitchell return ret; 435fce21744STroy Mitchell 436fce21744STroy Mitchell ret = devm_snd_soc_register_component(i2s->dev, 437fce21744STroy Mitchell &spacemit_i2s_component, 438fce21744STroy Mitchell dai, 1); 439fce21744STroy Mitchell if (ret) 440fce21744STroy Mitchell return dev_err_probe(i2s->dev, ret, "failed to register component"); 441fce21744STroy Mitchell 442fce21744STroy Mitchell return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); 443fce21744STroy Mitchell } 444fce21744STroy Mitchell 445fce21744STroy Mitchell static const struct of_device_id spacemit_i2s_of_match[] = { 446fce21744STroy Mitchell { .compatible = "spacemit,k1-i2s", }, 447fce21744STroy Mitchell { /* sentinel */ } 448fce21744STroy Mitchell }; 449fce21744STroy Mitchell MODULE_DEVICE_TABLE(of, spacemit_i2s_of_match); 450fce21744STroy Mitchell 451fce21744STroy Mitchell static struct platform_driver spacemit_i2s_driver = { 452fce21744STroy Mitchell .probe = spacemit_i2s_probe, 453fce21744STroy Mitchell .driver = { 454fce21744STroy Mitchell .name = "i2s-k1", 455fce21744STroy Mitchell .of_match_table = spacemit_i2s_of_match, 456fce21744STroy Mitchell }, 457fce21744STroy Mitchell }; 458fce21744STroy Mitchell module_platform_driver(spacemit_i2s_driver); 459fce21744STroy Mitchell 460fce21744STroy Mitchell MODULE_LICENSE("GPL"); 461fce21744STroy Mitchell MODULE_DESCRIPTION("I2S bus driver for SpacemiT K1 SoC"); 462