1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * jh7110_tdm.c -- StarFive JH7110 TDM driver 4 * 5 * Copyright (C) 2023 StarFive Technology Co., Ltd. 6 * 7 * Author: Walker Chen <walker.chen@starfivetech.com> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/device.h> 12 #include <linux/dmaengine.h> 13 #include <linux/module.h> 14 #include <linux/of_irq.h> 15 #include <linux/of_platform.h> 16 #include <linux/pm_runtime.h> 17 #include <linux/regmap.h> 18 #include <linux/reset.h> 19 #include <linux/types.h> 20 #include <sound/dmaengine_pcm.h> 21 #include <sound/initval.h> 22 #include <sound/pcm.h> 23 #include <sound/pcm_params.h> 24 #include <sound/soc.h> 25 #include <sound/soc-dai.h> 26 27 #define TDM_PCMGBCR 0x00 28 #define PCMGBCR_ENABLE BIT(0) 29 #define CLKPOL_BIT 5 30 #define ELM_BIT 3 31 #define SYNCM_BIT 2 32 #define MS_BIT 1 33 #define TDM_PCMTXCR 0x04 34 #define PCMTXCR_TXEN BIT(0) 35 #define IFL_BIT 11 36 #define WL_BIT 8 37 #define SSCALE_BIT 4 38 #define SL_BIT 2 39 #define LRJ_BIT 1 40 #define TDM_PCMRXCR 0x08 41 #define PCMRXCR_RXEN BIT(0) 42 #define TDM_PCMDIV 0x0c 43 44 #define JH7110_TDM_FIFO 0x170c0000 45 #define JH7110_TDM_FIFO_DEPTH 32 46 47 enum TDM_MASTER_SLAVE_MODE { 48 TDM_AS_MASTER = 0, 49 TDM_AS_SLAVE, 50 }; 51 52 enum TDM_CLKPOL { 53 /* tx raising and rx falling */ 54 TDM_TX_RASING_RX_FALLING = 0, 55 /* tx falling and rx raising */ 56 TDM_TX_FALLING_RX_RASING, 57 }; 58 59 enum TDM_ELM { 60 /* only work while SYNCM=0 */ 61 TDM_ELM_LATE = 0, 62 TDM_ELM_EARLY, 63 }; 64 65 enum TDM_SYNCM { 66 /* short frame sync */ 67 TDM_SYNCM_SHORT = 0, 68 /* long frame sync */ 69 TDM_SYNCM_LONG, 70 }; 71 72 enum TDM_IFL { 73 /* FIFO to send or received : half-1/2, Quarter-1/4 */ 74 TDM_FIFO_HALF = 0, 75 TDM_FIFO_QUARTER, 76 }; 77 78 enum TDM_WL { 79 /* send or received word length */ 80 TDM_8BIT_WORD_LEN = 0, 81 TDM_16BIT_WORD_LEN, 82 TDM_20BIT_WORD_LEN, 83 TDM_24BIT_WORD_LEN, 84 TDM_32BIT_WORD_LEN, 85 }; 86 87 enum TDM_SL { 88 /* send or received slot length */ 89 TDM_8BIT_SLOT_LEN = 0, 90 TDM_16BIT_SLOT_LEN, 91 TDM_32BIT_SLOT_LEN, 92 }; 93 94 enum TDM_LRJ { 95 /* left-justify or right-justify */ 96 TDM_RIGHT_JUSTIFY = 0, 97 TDM_LEFT_JUSTIFT, 98 }; 99 100 struct tdm_chan_cfg { 101 enum TDM_IFL ifl; 102 enum TDM_WL wl; 103 unsigned char sscale; 104 enum TDM_SL sl; 105 enum TDM_LRJ lrj; 106 unsigned char enable; 107 }; 108 109 struct jh7110_tdm_dev { 110 void __iomem *tdm_base; 111 struct device *dev; 112 struct clk_bulk_data clks[6]; 113 struct reset_control *resets; 114 115 enum TDM_CLKPOL clkpolity; 116 enum TDM_ELM elm; 117 enum TDM_SYNCM syncm; 118 enum TDM_MASTER_SLAVE_MODE ms_mode; 119 120 struct tdm_chan_cfg tx; 121 struct tdm_chan_cfg rx; 122 123 u16 syncdiv; 124 u32 samplerate; 125 u32 pcmclk; 126 127 /* data related to DMA transfers b/w tdm and DMAC */ 128 struct snd_dmaengine_dai_dma_data play_dma_data; 129 struct snd_dmaengine_dai_dma_data capture_dma_data; 130 u32 saved_pcmgbcr; 131 u32 saved_pcmtxcr; 132 u32 saved_pcmrxcr; 133 u32 saved_pcmdiv; 134 }; 135 136 static inline u32 jh7110_tdm_readl(struct jh7110_tdm_dev *tdm, u16 reg) 137 { 138 return readl_relaxed(tdm->tdm_base + reg); 139 } 140 141 static inline void jh7110_tdm_writel(struct jh7110_tdm_dev *tdm, u16 reg, u32 val) 142 { 143 writel_relaxed(val, tdm->tdm_base + reg); 144 } 145 146 static void jh7110_tdm_save_context(struct jh7110_tdm_dev *tdm, 147 struct snd_pcm_substream *substream) 148 { 149 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 150 tdm->saved_pcmtxcr = jh7110_tdm_readl(tdm, TDM_PCMTXCR); 151 else 152 tdm->saved_pcmrxcr = jh7110_tdm_readl(tdm, TDM_PCMRXCR); 153 } 154 155 static void jh7110_tdm_start(struct jh7110_tdm_dev *tdm, 156 struct snd_pcm_substream *substream) 157 { 158 u32 data; 159 160 data = jh7110_tdm_readl(tdm, TDM_PCMGBCR); 161 jh7110_tdm_writel(tdm, TDM_PCMGBCR, data | PCMGBCR_ENABLE); 162 163 /* restore context */ 164 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 165 jh7110_tdm_writel(tdm, TDM_PCMTXCR, tdm->saved_pcmtxcr | PCMTXCR_TXEN); 166 else 167 jh7110_tdm_writel(tdm, TDM_PCMRXCR, tdm->saved_pcmrxcr | PCMRXCR_RXEN); 168 } 169 170 static void jh7110_tdm_stop(struct jh7110_tdm_dev *tdm, 171 struct snd_pcm_substream *substream) 172 { 173 unsigned int val; 174 175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 176 val = jh7110_tdm_readl(tdm, TDM_PCMTXCR); 177 val &= ~PCMTXCR_TXEN; 178 jh7110_tdm_writel(tdm, TDM_PCMTXCR, val); 179 } else { 180 val = jh7110_tdm_readl(tdm, TDM_PCMRXCR); 181 val &= ~PCMRXCR_RXEN; 182 jh7110_tdm_writel(tdm, TDM_PCMRXCR, val); 183 } 184 } 185 186 static int jh7110_tdm_syncdiv(struct jh7110_tdm_dev *tdm) 187 { 188 u32 sl, sscale, syncdiv; 189 190 if (tdm->rx.sl >= tdm->tx.sl) 191 sl = tdm->rx.sl; 192 else 193 sl = tdm->tx.sl; 194 195 if (tdm->rx.sscale >= tdm->tx.sscale) 196 sscale = tdm->rx.sscale; 197 else 198 sscale = tdm->tx.sscale; 199 200 syncdiv = tdm->pcmclk / tdm->samplerate - 1; 201 202 if ((syncdiv + 1) < (sl * sscale)) { 203 dev_err(tdm->dev, "Failed to set syncdiv!\n"); 204 return -EINVAL; 205 } 206 207 if (tdm->syncm == TDM_SYNCM_LONG && 208 (tdm->rx.sscale <= 1 || tdm->tx.sscale <= 1) && 209 ((syncdiv + 1) <= sl)) { 210 dev_err(tdm->dev, "Wrong syncdiv! It must be (syncdiv+1) > max[tx.sl, rx.sl]\n"); 211 return -EINVAL; 212 } 213 214 jh7110_tdm_writel(tdm, TDM_PCMDIV, syncdiv); 215 return 0; 216 } 217 218 static int jh7110_tdm_config(struct jh7110_tdm_dev *tdm, 219 struct snd_pcm_substream *substream) 220 { 221 u32 datarx, datatx; 222 int ret; 223 224 ret = jh7110_tdm_syncdiv(tdm); 225 if (ret) 226 return ret; 227 228 datarx = (tdm->rx.ifl << IFL_BIT) | 229 (tdm->rx.wl << WL_BIT) | 230 (tdm->rx.sscale << SSCALE_BIT) | 231 (tdm->rx.sl << SL_BIT) | 232 (tdm->rx.lrj << LRJ_BIT); 233 234 datatx = (tdm->tx.ifl << IFL_BIT) | 235 (tdm->tx.wl << WL_BIT) | 236 (tdm->tx.sscale << SSCALE_BIT) | 237 (tdm->tx.sl << SL_BIT) | 238 (tdm->tx.lrj << LRJ_BIT); 239 240 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 241 jh7110_tdm_writel(tdm, TDM_PCMTXCR, datatx); 242 else 243 jh7110_tdm_writel(tdm, TDM_PCMRXCR, datarx); 244 245 return 0; 246 } 247 248 static void jh7110_tdm_clk_disable(struct jh7110_tdm_dev *tdm) 249 { 250 clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks); 251 } 252 253 static int jh7110_tdm_clk_enable(struct jh7110_tdm_dev *tdm) 254 { 255 int ret; 256 257 ret = clk_bulk_prepare_enable(ARRAY_SIZE(tdm->clks), tdm->clks); 258 if (ret) { 259 dev_err(tdm->dev, "Failed to enable tdm clocks\n"); 260 return ret; 261 } 262 263 ret = reset_control_deassert(tdm->resets); 264 if (ret) { 265 dev_err(tdm->dev, "Failed to deassert tdm resets\n"); 266 goto dis_tdm_clk; 267 } 268 269 /* select tdm_ext clock as the clock source for tdm */ 270 ret = clk_set_parent(tdm->clks[5].clk, tdm->clks[4].clk); 271 if (ret) { 272 dev_err(tdm->dev, "Can't set extern clock source for clk_tdm\n"); 273 goto dis_tdm_clk; 274 } 275 276 return 0; 277 278 dis_tdm_clk: 279 clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks); 280 281 return ret; 282 } 283 284 static int jh7110_tdm_runtime_suspend(struct device *dev) 285 { 286 struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); 287 288 jh7110_tdm_clk_disable(tdm); 289 return 0; 290 } 291 292 static int jh7110_tdm_runtime_resume(struct device *dev) 293 { 294 struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); 295 296 return jh7110_tdm_clk_enable(tdm); 297 } 298 299 static int jh7110_tdm_system_suspend(struct device *dev) 300 { 301 struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); 302 303 /* save context */ 304 tdm->saved_pcmgbcr = jh7110_tdm_readl(tdm, TDM_PCMGBCR); 305 tdm->saved_pcmdiv = jh7110_tdm_readl(tdm, TDM_PCMDIV); 306 307 return pm_runtime_force_suspend(dev); 308 } 309 310 static int jh7110_tdm_system_resume(struct device *dev) 311 { 312 struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); 313 314 /* restore context */ 315 jh7110_tdm_writel(tdm, TDM_PCMGBCR, tdm->saved_pcmgbcr); 316 jh7110_tdm_writel(tdm, TDM_PCMDIV, tdm->saved_pcmdiv); 317 318 return pm_runtime_force_resume(dev); 319 } 320 321 static const struct snd_soc_component_driver jh7110_tdm_component = { 322 .name = "jh7110-tdm", 323 }; 324 325 static int jh7110_tdm_startup(struct snd_pcm_substream *substream, 326 struct snd_soc_dai *cpu_dai) 327 { 328 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 329 struct snd_soc_dai_link *dai_link = rtd->dai_link; 330 331 dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; 332 333 return 0; 334 } 335 336 static int jh7110_tdm_hw_params(struct snd_pcm_substream *substream, 337 struct snd_pcm_hw_params *params, 338 struct snd_soc_dai *dai) 339 { 340 struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); 341 int chan_wl, chan_sl, chan_nr; 342 unsigned int data_width; 343 unsigned int dma_bus_width; 344 struct snd_dmaengine_dai_dma_data *dma_data = NULL; 345 int ret; 346 347 data_width = params_width(params); 348 349 tdm->samplerate = params_rate(params); 350 tdm->pcmclk = params_channels(params) * tdm->samplerate * data_width; 351 352 switch (params_format(params)) { 353 case SNDRV_PCM_FORMAT_S16_LE: 354 chan_wl = TDM_16BIT_WORD_LEN; 355 chan_sl = TDM_16BIT_SLOT_LEN; 356 dma_bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 357 break; 358 359 case SNDRV_PCM_FORMAT_S32_LE: 360 chan_wl = TDM_32BIT_WORD_LEN; 361 chan_sl = TDM_32BIT_SLOT_LEN; 362 dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 363 break; 364 365 default: 366 dev_err(tdm->dev, "tdm: unsupported PCM fmt"); 367 return -EINVAL; 368 } 369 370 chan_nr = params_channels(params); 371 switch (chan_nr) { 372 case 1: 373 case 2: 374 case 4: 375 case 6: 376 case 8: 377 break; 378 default: 379 dev_err(tdm->dev, "channel not supported\n"); 380 return -EINVAL; 381 } 382 383 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 384 tdm->tx.wl = chan_wl; 385 tdm->tx.sl = chan_sl; 386 tdm->tx.sscale = chan_nr; 387 tdm->play_dma_data.addr_width = dma_bus_width; 388 dma_data = &tdm->play_dma_data; 389 } else { 390 tdm->rx.wl = chan_wl; 391 tdm->rx.sl = chan_sl; 392 tdm->rx.sscale = chan_nr; 393 tdm->capture_dma_data.addr_width = dma_bus_width; 394 dma_data = &tdm->capture_dma_data; 395 } 396 397 snd_soc_dai_set_dma_data(dai, substream, dma_data); 398 399 ret = jh7110_tdm_config(tdm, substream); 400 if (ret) 401 return ret; 402 403 jh7110_tdm_save_context(tdm, substream); 404 return 0; 405 } 406 407 static int jh7110_tdm_trigger(struct snd_pcm_substream *substream, 408 int cmd, struct snd_soc_dai *dai) 409 { 410 struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); 411 int ret = 0; 412 413 switch (cmd) { 414 case SNDRV_PCM_TRIGGER_START: 415 case SNDRV_PCM_TRIGGER_RESUME: 416 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 417 jh7110_tdm_start(tdm, substream); 418 break; 419 420 case SNDRV_PCM_TRIGGER_STOP: 421 case SNDRV_PCM_TRIGGER_SUSPEND: 422 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 423 jh7110_tdm_stop(tdm, substream); 424 break; 425 default: 426 ret = -EINVAL; 427 break; 428 } 429 430 return ret; 431 } 432 433 static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, 434 unsigned int fmt) 435 { 436 struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(cpu_dai); 437 unsigned int gbcr; 438 439 /* set master/slave audio interface */ 440 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 441 case SND_SOC_DAIFMT_BP_FP: 442 /* cpu is master */ 443 tdm->ms_mode = TDM_AS_MASTER; 444 break; 445 case SND_SOC_DAIFMT_BC_FC: 446 /* codec is master */ 447 tdm->ms_mode = TDM_AS_SLAVE; 448 break; 449 case SND_SOC_DAIFMT_BC_FP: 450 case SND_SOC_DAIFMT_BP_FC: 451 return -EINVAL; 452 default: 453 dev_dbg(tdm->dev, "dwc : Invalid clock provider format\n"); 454 return -EINVAL; 455 } 456 457 gbcr = (tdm->clkpolity << CLKPOL_BIT) | 458 (tdm->elm << ELM_BIT) | 459 (tdm->syncm << SYNCM_BIT) | 460 (tdm->ms_mode << MS_BIT); 461 jh7110_tdm_writel(tdm, TDM_PCMGBCR, gbcr); 462 463 return 0; 464 } 465 466 static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) 467 { 468 struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); 469 470 snd_soc_dai_init_dma_data(dai, &tdm->play_dma_data, &tdm->capture_dma_data); 471 snd_soc_dai_set_drvdata(dai, tdm); 472 return 0; 473 } 474 475 static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { 476 .probe = jh7110_tdm_dai_probe, 477 .startup = jh7110_tdm_startup, 478 .hw_params = jh7110_tdm_hw_params, 479 .trigger = jh7110_tdm_trigger, 480 .set_fmt = jh7110_tdm_set_dai_fmt, 481 }; 482 483 #define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000 484 485 #define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 486 SNDRV_PCM_FMTBIT_S32_LE) 487 488 static struct snd_soc_dai_driver jh7110_tdm_dai = { 489 .name = "sf_tdm", 490 .id = 0, 491 .playback = { 492 .stream_name = "Playback", 493 .channels_min = 1, 494 .channels_max = 8, 495 .rates = JH7110_TDM_RATES, 496 .formats = JH7110_TDM_FORMATS, 497 }, 498 .capture = { 499 .stream_name = "Capture", 500 .channels_min = 1, 501 .channels_max = 8, 502 .rates = JH7110_TDM_RATES, 503 .formats = JH7110_TDM_FORMATS, 504 }, 505 .ops = &jh7110_tdm_dai_ops, 506 .symmetric_rate = 1, 507 }; 508 509 static const struct snd_pcm_hardware jh7110_pcm_hardware = { 510 .info = (SNDRV_PCM_INFO_MMAP | 511 SNDRV_PCM_INFO_MMAP_VALID | 512 SNDRV_PCM_INFO_PAUSE | 513 SNDRV_PCM_INFO_RESUME | 514 SNDRV_PCM_INFO_INTERLEAVED | 515 SNDRV_PCM_INFO_BLOCK_TRANSFER), 516 .buffer_bytes_max = 192512, 517 .period_bytes_min = 4096, 518 .period_bytes_max = 32768, 519 .periods_min = 1, 520 .periods_max = 48, 521 .fifo_size = 16, 522 }; 523 524 static const struct snd_dmaengine_pcm_config jh7110_dmaengine_pcm_config = { 525 .pcm_hardware = &jh7110_pcm_hardware, 526 .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 527 .prealloc_buffer_size = 192512, 528 }; 529 530 static void jh7110_tdm_init_params(struct jh7110_tdm_dev *tdm) 531 { 532 tdm->clkpolity = TDM_TX_RASING_RX_FALLING; 533 tdm->elm = TDM_ELM_LATE; 534 tdm->syncm = TDM_SYNCM_SHORT; 535 536 tdm->rx.ifl = TDM_FIFO_HALF; 537 tdm->tx.ifl = TDM_FIFO_HALF; 538 tdm->rx.wl = TDM_16BIT_WORD_LEN; 539 tdm->tx.wl = TDM_16BIT_WORD_LEN; 540 tdm->rx.sscale = 2; 541 tdm->tx.sscale = 2; 542 tdm->rx.lrj = TDM_LEFT_JUSTIFT; 543 tdm->tx.lrj = TDM_LEFT_JUSTIFT; 544 545 tdm->play_dma_data.addr = JH7110_TDM_FIFO; 546 tdm->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 547 tdm->play_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2; 548 tdm->play_dma_data.maxburst = 16; 549 550 tdm->capture_dma_data.addr = JH7110_TDM_FIFO; 551 tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 552 tdm->capture_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2; 553 tdm->capture_dma_data.maxburst = 8; 554 } 555 556 static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, 557 struct jh7110_tdm_dev *tdm) 558 { 559 int ret; 560 561 tdm->clks[0].id = "mclk_inner"; 562 tdm->clks[1].id = "tdm_ahb"; 563 tdm->clks[2].id = "tdm_apb"; 564 tdm->clks[3].id = "tdm_internal"; 565 tdm->clks[4].id = "tdm_ext"; 566 tdm->clks[5].id = "tdm"; 567 568 ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); 569 if (ret) { 570 dev_err(&pdev->dev, "Failed to get tdm clocks\n"); 571 return ret; 572 } 573 574 tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); 575 if (IS_ERR(tdm->resets)) { 576 dev_err(&pdev->dev, "Failed to get tdm resets\n"); 577 return PTR_ERR(tdm->resets); 578 } 579 580 return 0; 581 } 582 583 static int jh7110_tdm_probe(struct platform_device *pdev) 584 { 585 struct jh7110_tdm_dev *tdm; 586 int ret; 587 588 tdm = devm_kzalloc(&pdev->dev, sizeof(*tdm), GFP_KERNEL); 589 if (!tdm) 590 return -ENOMEM; 591 592 tdm->tdm_base = devm_platform_ioremap_resource(pdev, 0); 593 if (IS_ERR(tdm->tdm_base)) 594 return PTR_ERR(tdm->tdm_base); 595 596 tdm->dev = &pdev->dev; 597 598 ret = jh7110_tdm_clk_reset_get(pdev, tdm); 599 if (ret) { 600 dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); 601 return ret; 602 } 603 604 jh7110_tdm_init_params(tdm); 605 606 dev_set_drvdata(&pdev->dev, tdm); 607 ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, 608 &jh7110_tdm_dai, 1); 609 if (ret) { 610 dev_err(&pdev->dev, "Failed to register dai\n"); 611 return ret; 612 } 613 614 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 615 &jh7110_dmaengine_pcm_config, 616 SND_DMAENGINE_PCM_FLAG_COMPAT); 617 if (ret) { 618 dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); 619 return ret; 620 } 621 622 pm_runtime_enable(&pdev->dev); 623 if (!pm_runtime_enabled(&pdev->dev)) { 624 ret = jh7110_tdm_runtime_resume(&pdev->dev); 625 if (ret) 626 goto err_pm_disable; 627 } 628 629 return 0; 630 631 err_pm_disable: 632 pm_runtime_disable(&pdev->dev); 633 634 return ret; 635 } 636 637 static void jh7110_tdm_dev_remove(struct platform_device *pdev) 638 { 639 pm_runtime_disable(&pdev->dev); 640 } 641 642 static const struct of_device_id jh7110_tdm_of_match[] = { 643 { .compatible = "starfive,jh7110-tdm", }, 644 {} 645 }; 646 647 MODULE_DEVICE_TABLE(of, jh7110_tdm_of_match); 648 649 static const struct dev_pm_ops jh7110_tdm_pm_ops = { 650 RUNTIME_PM_OPS(jh7110_tdm_runtime_suspend, 651 jh7110_tdm_runtime_resume, NULL) 652 SYSTEM_SLEEP_PM_OPS(jh7110_tdm_system_suspend, 653 jh7110_tdm_system_resume) 654 }; 655 656 static struct platform_driver jh7110_tdm_driver = { 657 .driver = { 658 .name = "jh7110-tdm", 659 .of_match_table = jh7110_tdm_of_match, 660 .pm = pm_ptr(&jh7110_tdm_pm_ops), 661 }, 662 .probe = jh7110_tdm_probe, 663 .remove_new = jh7110_tdm_dev_remove, 664 }; 665 module_platform_driver(jh7110_tdm_driver); 666 667 MODULE_DESCRIPTION("StarFive JH7110 TDM ASoC Driver"); 668 MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>"); 669 MODULE_LICENSE("GPL"); 670
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.