Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add PWM-DAC audio support for StarFive JH7110

Merge series from Hal Feng <hal.feng@starfivetech.com>:

This patchset adds PWM-DAC audio support for the StarFive JH7110 SoC.
The PWM-DAC module does not require a hardware codec, but a dummy codec is
needed for the driver. The dummy spdif codec driver, which is already
upstream, is compatible with the one which JH7110 PWM-DAC needed. So we
use it as the dummy codec driver for the JH7110 PWM-DAC module.

+622
+76
Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: StarFive JH7110 PWM-DAC Controller 8 + 9 + description: 10 + The PWM-DAC Controller uses PWM square wave generators plus RC filters to 11 + form a DAC for audio play in StarFive JH7110 SoC. This audio play controller 12 + supports 16 bit audio format, up to 48K sampling frequency, up to left and 13 + right dual channels. 14 + 15 + maintainers: 16 + - Hal Feng <hal.feng@starfivetech.com> 17 + 18 + allOf: 19 + - $ref: dai-common.yaml# 20 + 21 + properties: 22 + compatible: 23 + const: starfive,jh7110-pwmdac 24 + 25 + reg: 26 + maxItems: 1 27 + 28 + clocks: 29 + items: 30 + - description: PWMDAC APB 31 + - description: PWMDAC CORE 32 + 33 + clock-names: 34 + items: 35 + - const: apb 36 + - const: core 37 + 38 + resets: 39 + maxItems: 1 40 + description: PWMDAC APB 41 + 42 + dmas: 43 + maxItems: 1 44 + description: TX DMA Channel 45 + 46 + dma-names: 47 + const: tx 48 + 49 + "#sound-dai-cells": 50 + const: 0 51 + 52 + required: 53 + - compatible 54 + - reg 55 + - clocks 56 + - clock-names 57 + - resets 58 + - dmas 59 + - dma-names 60 + - "#sound-dai-cells" 61 + 62 + additionalProperties: false 63 + 64 + examples: 65 + - | 66 + pwmdac@100b0000 { 67 + compatible = "starfive,jh7110-pwmdac"; 68 + reg = <0x100b0000 0x1000>; 69 + clocks = <&syscrg 157>, 70 + <&syscrg 158>; 71 + clock-names = "apb", "core"; 72 + resets = <&syscrg 96>; 73 + dmas = <&dma 22>; 74 + dma-names = "tx"; 75 + #sound-dai-cells = <0>; 76 + };
+7
MAINTAINERS
··· 20466 20466 F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml 20467 20467 F: drivers/clk/starfive/clk-starfive-jh7110-pll.c 20468 20468 20469 + STARFIVE JH7110 PWMDAC DRIVER 20470 + M: Hal Feng <hal.feng@starfivetech.com> 20471 + M: Xingyu Wu <xingyu.wu@starfivetech.com> 20472 + S: Supported 20473 + F: Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml 20474 + F: sound/soc/starfive/jh7110_pwmdac.c 20475 + 20469 20476 STARFIVE JH7110 SYSCON 20470 20477 M: William Qiu <william.qiu@starfivetech.com> 20471 20478 M: Xingyu Wu <xingyu.wu@starfivetech.com>
+9
sound/soc/starfive/Kconfig
··· 7 7 the Starfive SoCs' Audio interfaces. You will also need to 8 8 select the audio interfaces to support below. 9 9 10 + config SND_SOC_JH7110_PWMDAC 11 + tristate "JH7110 PWM-DAC device driver" 12 + depends on HAVE_CLK && SND_SOC_STARFIVE 13 + select SND_SOC_GENERIC_DMAENGINE_PCM 14 + select SND_SOC_SPDIF 15 + help 16 + Say Y or M if you want to add support for StarFive JH7110 17 + PWM-DAC driver. 18 + 10 19 config SND_SOC_JH7110_TDM 11 20 tristate "JH7110 TDM device driver" 12 21 depends on HAVE_CLK && SND_SOC_STARFIVE
+1
sound/soc/starfive/Makefile
··· 1 1 # StarFive Platform Support 2 + obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o 2 3 obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
+529
sound/soc/starfive/jh7110_pwmdac.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver 4 + * 5 + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. 6 + * 7 + * Authors: Jenny Zhang 8 + * Curry Zhang 9 + * Xingyu Wu <xingyu.wu@starfivetech.com> 10 + * Hal Feng <hal.feng@starfivetech.com> 11 + */ 12 + 13 + #include <linux/clk.h> 14 + #include <linux/device.h> 15 + #include <linux/init.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/io.h> 18 + #include <linux/module.h> 19 + #include <linux/pm_runtime.h> 20 + #include <linux/reset.h> 21 + #include <linux/slab.h> 22 + #include <linux/types.h> 23 + #include <sound/dmaengine_pcm.h> 24 + #include <sound/pcm.h> 25 + #include <sound/pcm_params.h> 26 + #include <sound/soc.h> 27 + 28 + #define JH7110_PWMDAC_WDATA 0x00 29 + #define JH7110_PWMDAC_CTRL 0x04 30 + #define JH7110_PWMDAC_ENABLE BIT(0) 31 + #define JH7110_PWMDAC_SHIFT BIT(1) 32 + #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT 2 33 + #define JH7110_PWMDAC_DUTY_CYCLE_MASK GENMASK(3, 2) 34 + #define JH7110_PWMDAC_CNT_N_SHIFT 4 35 + #define JH7110_PWMDAC_CNT_N_MASK GENMASK(12, 4) 36 + #define JH7110_PWMDAC_DATA_CHANGE BIT(13) 37 + #define JH7110_PWMDAC_DATA_MODE BIT(14) 38 + #define JH7110_PWMDAC_DATA_SHIFT_SHIFT 15 39 + #define JH7110_PWMDAC_DATA_SHIFT_MASK GENMASK(17, 15) 40 + 41 + enum JH7110_PWMDAC_SHIFT_VAL { 42 + PWMDAC_SHIFT_8 = 0, 43 + PWMDAC_SHIFT_10, 44 + }; 45 + 46 + enum JH7110_PWMDAC_DUTY_CYCLE_VAL { 47 + PWMDAC_CYCLE_LEFT = 0, 48 + PWMDAC_CYCLE_RIGHT, 49 + PWMDAC_CYCLE_CENTER, 50 + }; 51 + 52 + enum JH7110_PWMDAC_CNT_N_VAL { 53 + PWMDAC_SAMPLE_CNT_1 = 1, 54 + PWMDAC_SAMPLE_CNT_2, 55 + PWMDAC_SAMPLE_CNT_3, 56 + PWMDAC_SAMPLE_CNT_512 = 512, /* max */ 57 + }; 58 + 59 + enum JH7110_PWMDAC_DATA_CHANGE_VAL { 60 + NO_CHANGE = 0, 61 + CHANGE, 62 + }; 63 + 64 + enum JH7110_PWMDAC_DATA_MODE_VAL { 65 + UNSIGNED_DATA = 0, 66 + INVERTER_DATA_MSB, 67 + }; 68 + 69 + enum JH7110_PWMDAC_DATA_SHIFT_VAL { 70 + PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0, 71 + PWMDAC_DATA_LEFT_SHIFT_BIT_1, 72 + PWMDAC_DATA_LEFT_SHIFT_BIT_2, 73 + PWMDAC_DATA_LEFT_SHIFT_BIT_3, 74 + PWMDAC_DATA_LEFT_SHIFT_BIT_4, 75 + PWMDAC_DATA_LEFT_SHIFT_BIT_5, 76 + PWMDAC_DATA_LEFT_SHIFT_BIT_6, 77 + PWMDAC_DATA_LEFT_SHIFT_BIT_7, 78 + }; 79 + 80 + struct jh7110_pwmdac_cfg { 81 + enum JH7110_PWMDAC_SHIFT_VAL shift; 82 + enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle; 83 + u16 cnt_n; 84 + enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change; 85 + enum JH7110_PWMDAC_DATA_MODE_VAL data_mode; 86 + enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift; 87 + }; 88 + 89 + struct jh7110_pwmdac_dev { 90 + void __iomem *base; 91 + resource_size_t mapbase; 92 + struct jh7110_pwmdac_cfg cfg; 93 + 94 + struct clk_bulk_data clks[2]; 95 + struct reset_control *rst_apb; 96 + struct device *dev; 97 + struct snd_dmaengine_dai_dma_data play_dma_data; 98 + u32 saved_ctrl; 99 + }; 100 + 101 + static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val) 102 + { 103 + writel(val, io_base + reg); 104 + } 105 + 106 + static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg) 107 + { 108 + return readl(io_base + reg); 109 + } 110 + 111 + static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable) 112 + { 113 + u32 value; 114 + 115 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 116 + if (enable) 117 + value |= JH7110_PWMDAC_ENABLE; 118 + else 119 + value &= ~JH7110_PWMDAC_ENABLE; 120 + 121 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 122 + } 123 + 124 + static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev) 125 + { 126 + u32 value; 127 + 128 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 129 + if (dev->cfg.shift == PWMDAC_SHIFT_8) 130 + value &= ~JH7110_PWMDAC_SHIFT; 131 + else if (dev->cfg.shift == PWMDAC_SHIFT_10) 132 + value |= JH7110_PWMDAC_SHIFT; 133 + 134 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 135 + } 136 + 137 + static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev) 138 + { 139 + u32 value; 140 + 141 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 142 + value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK; 143 + value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT; 144 + 145 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 146 + } 147 + 148 + static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev) 149 + { 150 + u32 value; 151 + 152 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 153 + value &= ~JH7110_PWMDAC_CNT_N_MASK; 154 + value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT; 155 + 156 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 157 + } 158 + 159 + static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev) 160 + { 161 + u32 value; 162 + 163 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 164 + if (dev->cfg.data_change == NO_CHANGE) 165 + value &= ~JH7110_PWMDAC_DATA_CHANGE; 166 + else if (dev->cfg.data_change == CHANGE) 167 + value |= JH7110_PWMDAC_DATA_CHANGE; 168 + 169 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 170 + } 171 + 172 + static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev) 173 + { 174 + u32 value; 175 + 176 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 177 + if (dev->cfg.data_mode == UNSIGNED_DATA) 178 + value &= ~JH7110_PWMDAC_DATA_MODE; 179 + else if (dev->cfg.data_mode == INVERTER_DATA_MSB) 180 + value |= JH7110_PWMDAC_DATA_MODE; 181 + 182 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 183 + } 184 + 185 + static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev) 186 + { 187 + u32 value; 188 + 189 + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); 190 + value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK; 191 + value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT; 192 + 193 + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); 194 + } 195 + 196 + static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev) 197 + { 198 + jh7110_pwmdac_set_shift(dev); 199 + jh7110_pwmdac_set_duty_cycle(dev); 200 + jh7110_pwmdac_set_cnt_n(dev); 201 + jh7110_pwmdac_set_enable(dev, true); 202 + 203 + jh7110_pwmdac_set_data_change(dev); 204 + jh7110_pwmdac_set_data_mode(dev); 205 + jh7110_pwmdac_set_data_shift(dev); 206 + } 207 + 208 + static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev) 209 + { 210 + jh7110_pwmdac_set_enable(dev, false); 211 + } 212 + 213 + static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream, 214 + struct snd_soc_dai *dai) 215 + { 216 + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 217 + struct snd_soc_dai_link *dai_link = rtd->dai_link; 218 + 219 + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; 220 + 221 + return 0; 222 + } 223 + 224 + static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream, 225 + struct snd_pcm_hw_params *params, 226 + struct snd_soc_dai *dai) 227 + { 228 + struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); 229 + unsigned long core_clk_rate; 230 + int ret; 231 + 232 + switch (params_rate(params)) { 233 + case 8000: 234 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; 235 + core_clk_rate = 6144000; 236 + break; 237 + case 11025: 238 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2; 239 + core_clk_rate = 5644800; 240 + break; 241 + case 16000: 242 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; 243 + core_clk_rate = 12288000; 244 + break; 245 + case 22050: 246 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; 247 + core_clk_rate = 5644800; 248 + break; 249 + case 32000: 250 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; 251 + core_clk_rate = 8192000; 252 + break; 253 + case 44100: 254 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; 255 + core_clk_rate = 11289600; 256 + break; 257 + case 48000: 258 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; 259 + core_clk_rate = 12288000; 260 + break; 261 + default: 262 + dev_err(dai->dev, "%d rate not supported\n", 263 + params_rate(params)); 264 + return -EINVAL; 265 + } 266 + 267 + switch (params_channels(params)) { 268 + case 1: 269 + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 270 + break; 271 + case 2: 272 + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 273 + break; 274 + default: 275 + dev_err(dai->dev, "%d channels not supported\n", 276 + params_channels(params)); 277 + return -EINVAL; 278 + } 279 + 280 + /* 281 + * The clock rate always rounds down when using clk_set_rate() 282 + * so increase the rate a bit 283 + */ 284 + core_clk_rate += 64; 285 + jh7110_pwmdac_set(dev); 286 + 287 + ret = clk_set_rate(dev->clks[1].clk, core_clk_rate); 288 + if (ret) 289 + return dev_err_probe(dai->dev, ret, 290 + "failed to set rate %lu for core clock\n", 291 + core_clk_rate); 292 + 293 + return 0; 294 + } 295 + 296 + static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd, 297 + struct snd_soc_dai *dai) 298 + { 299 + struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai); 300 + int ret = 0; 301 + 302 + switch (cmd) { 303 + case SNDRV_PCM_TRIGGER_START: 304 + case SNDRV_PCM_TRIGGER_RESUME: 305 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 306 + jh7110_pwmdac_set(dev); 307 + break; 308 + 309 + case SNDRV_PCM_TRIGGER_STOP: 310 + case SNDRV_PCM_TRIGGER_SUSPEND: 311 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 312 + jh7110_pwmdac_stop(dev); 313 + break; 314 + default: 315 + ret = -EINVAL; 316 + break; 317 + } 318 + 319 + return ret; 320 + } 321 + 322 + static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable) 323 + { 324 + int ret; 325 + 326 + if (enable) { 327 + ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks); 328 + if (ret) 329 + return dev_err_probe(dev->dev, ret, 330 + "failed to enable pwmdac clocks\n"); 331 + 332 + ret = reset_control_deassert(dev->rst_apb); 333 + if (ret) { 334 + dev_err(dev->dev, "failed to deassert pwmdac apb reset\n"); 335 + goto err_rst_apb; 336 + } 337 + } else { 338 + clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); 339 + } 340 + 341 + return 0; 342 + 343 + err_rst_apb: 344 + clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); 345 + 346 + return ret; 347 + } 348 + 349 + static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai) 350 + { 351 + struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); 352 + 353 + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); 354 + snd_soc_dai_set_drvdata(dai, dev); 355 + 356 + return 0; 357 + } 358 + 359 + static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { 360 + .startup = jh7110_pwmdac_startup, 361 + .hw_params = jh7110_pwmdac_hw_params, 362 + .trigger = jh7110_pwmdac_trigger, 363 + }; 364 + 365 + static const struct snd_soc_component_driver jh7110_pwmdac_component = { 366 + .name = "jh7110-pwmdac", 367 + }; 368 + 369 + static struct snd_soc_dai_driver jh7110_pwmdac_dai = { 370 + .name = "jh7110-pwmdac", 371 + .id = 0, 372 + .probe = jh7110_pwmdac_dai_probe, 373 + .playback = { 374 + .channels_min = 1, 375 + .channels_max = 2, 376 + .rates = SNDRV_PCM_RATE_8000_48000, 377 + .formats = SNDRV_PCM_FMTBIT_S16_LE, 378 + }, 379 + .ops = &jh7110_pwmdac_dai_ops, 380 + }; 381 + 382 + static int jh7110_pwmdac_runtime_suspend(struct device *dev) 383 + { 384 + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); 385 + 386 + return jh7110_pwmdac_crg_enable(pwmdac, false); 387 + } 388 + 389 + static int jh7110_pwmdac_runtime_resume(struct device *dev) 390 + { 391 + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); 392 + 393 + return jh7110_pwmdac_crg_enable(pwmdac, true); 394 + } 395 + 396 + static int jh7110_pwmdac_system_suspend(struct device *dev) 397 + { 398 + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); 399 + 400 + /* save the CTRL register value */ 401 + pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base, 402 + JH7110_PWMDAC_CTRL); 403 + return pm_runtime_force_suspend(dev); 404 + } 405 + 406 + static int jh7110_pwmdac_system_resume(struct device *dev) 407 + { 408 + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); 409 + int ret; 410 + 411 + ret = pm_runtime_force_resume(dev); 412 + if (ret) 413 + return ret; 414 + 415 + /* restore the CTRL register value */ 416 + jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL, 417 + pwmdac->saved_ctrl); 418 + return 0; 419 + } 420 + 421 + static const struct dev_pm_ops jh7110_pwmdac_pm_ops = { 422 + RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend, 423 + jh7110_pwmdac_runtime_resume, NULL) 424 + SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend, 425 + jh7110_pwmdac_system_resume) 426 + }; 427 + 428 + static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev) 429 + { 430 + dev->cfg.shift = PWMDAC_SHIFT_8; 431 + dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER; 432 + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; 433 + dev->cfg.data_change = NO_CHANGE; 434 + dev->cfg.data_mode = INVERTER_DATA_MSB; 435 + dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0; 436 + 437 + dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA; 438 + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 439 + dev->play_dma_data.fifo_size = 1; 440 + dev->play_dma_data.maxburst = 16; 441 + } 442 + 443 + static int jh7110_pwmdac_probe(struct platform_device *pdev) 444 + { 445 + struct jh7110_pwmdac_dev *dev; 446 + struct resource *res; 447 + int ret; 448 + 449 + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 450 + if (!dev) 451 + return -ENOMEM; 452 + 453 + dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 454 + if (IS_ERR(dev->base)) 455 + return PTR_ERR(dev->base); 456 + 457 + dev->mapbase = res->start; 458 + 459 + dev->clks[0].id = "apb"; 460 + dev->clks[1].id = "core"; 461 + 462 + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks); 463 + if (ret) 464 + return dev_err_probe(&pdev->dev, ret, 465 + "failed to get pwmdac clocks\n"); 466 + 467 + dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL); 468 + if (IS_ERR(dev->rst_apb)) 469 + return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb), 470 + "failed to get pwmdac apb reset\n"); 471 + 472 + jh7110_pwmdac_init_params(dev); 473 + 474 + dev->dev = &pdev->dev; 475 + dev_set_drvdata(&pdev->dev, dev); 476 + ret = devm_snd_soc_register_component(&pdev->dev, 477 + &jh7110_pwmdac_component, 478 + &jh7110_pwmdac_dai, 1); 479 + if (ret) 480 + return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); 481 + 482 + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 483 + if (ret) 484 + return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); 485 + 486 + pm_runtime_enable(dev->dev); 487 + if (!pm_runtime_enabled(&pdev->dev)) { 488 + ret = jh7110_pwmdac_runtime_resume(&pdev->dev); 489 + if (ret) 490 + goto err_pm_disable; 491 + } 492 + 493 + return 0; 494 + 495 + err_pm_disable: 496 + pm_runtime_disable(&pdev->dev); 497 + 498 + return ret; 499 + } 500 + 501 + static int jh7110_pwmdac_remove(struct platform_device *pdev) 502 + { 503 + pm_runtime_disable(&pdev->dev); 504 + return 0; 505 + } 506 + 507 + static const struct of_device_id jh7110_pwmdac_of_match[] = { 508 + { .compatible = "starfive,jh7110-pwmdac" }, 509 + { /* sentinel */ } 510 + }; 511 + MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match); 512 + 513 + static struct platform_driver jh7110_pwmdac_driver = { 514 + .driver = { 515 + .name = "jh7110-pwmdac", 516 + .of_match_table = jh7110_pwmdac_of_match, 517 + .pm = pm_ptr(&jh7110_pwmdac_pm_ops), 518 + }, 519 + .probe = jh7110_pwmdac_probe, 520 + .remove = jh7110_pwmdac_remove, 521 + }; 522 + module_platform_driver(jh7110_pwmdac_driver); 523 + 524 + MODULE_AUTHOR("Jenny Zhang"); 525 + MODULE_AUTHOR("Curry Zhang"); 526 + MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); 527 + MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>"); 528 + MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver"); 529 + MODULE_LICENSE("GPL");