1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * IMG I2S output controller driver 4 * 5 * Copyright (C) 2015 Imagination Technologies Ltd. 6 * 7 * Author: Damien Horsley <Damien.Horsley@imgtec.com> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/init.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/pm_runtime.h> 17 #include <linux/reset.h> 18 19 #include <sound/core.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 26 #define IMG_I2S_OUT_TX_FIFO 0x0 27 28 #define IMG_I2S_OUT_CTL 0x4 29 #define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24) 30 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000 31 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13 32 #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8) 33 #define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6) 34 #define IMG_I2S_OUT_CTL_CLK_MASK BIT(5) 35 #define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4) 36 #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3) 37 #define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2) 38 #define IMG_I2S_OUT_CTL_ME_MASK BIT(0) 39 40 #define IMG_I2S_OUT_CH_CTL 0x4 41 #define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11) 42 #define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10) 43 #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0 44 #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4 45 #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3) 46 #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1) 47 #define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0) 48 49 #define IMG_I2S_OUT_CH_STRIDE 0x20 50 51 struct img_i2s_out { 52 void __iomem *base; 53 struct clk *clk_sys; 54 struct clk *clk_ref; 55 struct snd_dmaengine_dai_dma_data dma_data; 56 struct device *dev; 57 unsigned int max_i2s_chan; 58 void __iomem *channel_base; 59 bool force_clk_active; 60 unsigned int active_channels; 61 struct reset_control *rst; 62 struct snd_soc_dai_driver dai_driver; 63 u32 suspend_ctl; 64 u32 *suspend_ch_ctl; 65 }; 66 67 static int img_i2s_out_runtime_suspend(struct device *dev) 68 { 69 struct img_i2s_out *i2s = dev_get_drvdata(dev); 70 71 clk_disable_unprepare(i2s->clk_ref); 72 clk_disable_unprepare(i2s->clk_sys); 73 74 return 0; 75 } 76 77 static int img_i2s_out_runtime_resume(struct device *dev) 78 { 79 struct img_i2s_out *i2s = dev_get_drvdata(dev); 80 int ret; 81 82 ret = clk_prepare_enable(i2s->clk_sys); 83 if (ret) { 84 dev_err(dev, "clk_enable failed: %d\n", ret); 85 return ret; 86 } 87 88 ret = clk_prepare_enable(i2s->clk_ref); 89 if (ret) { 90 dev_err(dev, "clk_enable failed: %d\n", ret); 91 clk_disable_unprepare(i2s->clk_sys); 92 return ret; 93 } 94 95 return 0; 96 } 97 98 static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val, 99 u32 reg) 100 { 101 writel(val, i2s->base + reg); 102 } 103 104 static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg) 105 { 106 return readl(i2s->base + reg); 107 } 108 109 static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s, 110 u32 chan, u32 val, u32 reg) 111 { 112 writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); 113 } 114 115 static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan, 116 u32 reg) 117 { 118 return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); 119 } 120 121 static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan) 122 { 123 u32 reg; 124 125 reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); 126 reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; 127 img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); 128 } 129 130 static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan) 131 { 132 u32 reg; 133 134 reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); 135 reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK; 136 img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); 137 } 138 139 static inline void img_i2s_out_disable(struct img_i2s_out *i2s) 140 { 141 u32 reg; 142 143 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 144 reg &= ~IMG_I2S_OUT_CTL_ME_MASK; 145 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 146 } 147 148 static inline void img_i2s_out_enable(struct img_i2s_out *i2s) 149 { 150 u32 reg; 151 152 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 153 reg |= IMG_I2S_OUT_CTL_ME_MASK; 154 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 155 } 156 157 static void img_i2s_out_reset(struct img_i2s_out *i2s) 158 { 159 int i; 160 u32 core_ctl, chan_ctl; 161 162 core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) & 163 ~IMG_I2S_OUT_CTL_ME_MASK & 164 ~IMG_I2S_OUT_CTL_DATA_EN_MASK; 165 166 if (!i2s->force_clk_active) 167 core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK; 168 169 chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) & 170 ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; 171 172 reset_control_assert(i2s->rst); 173 reset_control_deassert(i2s->rst); 174 175 for (i = 0; i < i2s->max_i2s_chan; i++) 176 img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL); 177 178 for (i = 0; i < i2s->active_channels; i++) 179 img_i2s_out_ch_enable(i2s, i); 180 181 img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL); 182 img_i2s_out_enable(i2s); 183 } 184 185 static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd, 186 struct snd_soc_dai *dai) 187 { 188 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 189 u32 reg; 190 191 switch (cmd) { 192 case SNDRV_PCM_TRIGGER_START: 193 case SNDRV_PCM_TRIGGER_RESUME: 194 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 195 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 196 if (!i2s->force_clk_active) 197 reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK; 198 reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK; 199 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 200 break; 201 case SNDRV_PCM_TRIGGER_STOP: 202 case SNDRV_PCM_TRIGGER_SUSPEND: 203 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 204 img_i2s_out_reset(i2s); 205 break; 206 default: 207 return -EINVAL; 208 } 209 210 return 0; 211 } 212 213 static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, 214 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 215 { 216 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 217 unsigned int channels, i2s_channels; 218 long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; 219 int i; 220 u32 reg, control_mask, control_set = 0; 221 snd_pcm_format_t format; 222 223 rate = params_rate(params); 224 format = params_format(params); 225 channels = params_channels(params); 226 i2s_channels = channels / 2; 227 228 if (format != SNDRV_PCM_FORMAT_S32_LE) 229 return -EINVAL; 230 231 if ((channels < 2) || 232 (channels > (i2s->max_i2s_chan * 2)) || 233 (channels % 2)) 234 return -EINVAL; 235 236 pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256); 237 if (pre_div_a < 0) 238 return pre_div_a; 239 pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384); 240 if (pre_div_b < 0) 241 return pre_div_b; 242 243 diff_a = abs((pre_div_a / 256) - rate); 244 diff_b = abs((pre_div_b / 384) - rate); 245 246 /* If diffs are equal, use lower clock rate */ 247 if (diff_a > diff_b) 248 clk_set_rate(i2s->clk_ref, pre_div_b); 249 else 250 clk_set_rate(i2s->clk_ref, pre_div_a); 251 252 /* 253 * Another driver (eg alsa machine driver) may have rejected the above 254 * change. Get the current rate and set the register bit according to 255 * the new minimum diff 256 */ 257 clk_rate = clk_get_rate(i2s->clk_ref); 258 259 diff_a = abs((clk_rate / 256) - rate); 260 diff_b = abs((clk_rate / 384) - rate); 261 262 if (diff_a > diff_b) 263 control_set |= IMG_I2S_OUT_CTL_CLK_MASK; 264 265 control_set |= ((i2s_channels - 1) << 266 IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) & 267 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; 268 269 control_mask = IMG_I2S_OUT_CTL_CLK_MASK | 270 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; 271 272 img_i2s_out_disable(i2s); 273 274 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 275 reg = (reg & ~control_mask) | control_set; 276 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 277 278 for (i = 0; i < i2s_channels; i++) 279 img_i2s_out_ch_enable(i2s, i); 280 281 for (; i < i2s->max_i2s_chan; i++) 282 img_i2s_out_ch_disable(i2s, i); 283 284 img_i2s_out_enable(i2s); 285 286 i2s->active_channels = i2s_channels; 287 288 return 0; 289 } 290 291 static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 292 { 293 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 294 int i, ret; 295 bool force_clk_active; 296 u32 chan_control_mask, control_mask, chan_control_set = 0; 297 u32 reg, control_set = 0; 298 299 force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) == 300 SND_SOC_DAIFMT_CONT); 301 302 if (force_clk_active) 303 control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK; 304 305 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 306 case SND_SOC_DAIFMT_BC_FC: 307 break; 308 case SND_SOC_DAIFMT_BP_FP: 309 control_set |= IMG_I2S_OUT_CTL_MASTER_MASK; 310 break; 311 default: 312 return -EINVAL; 313 } 314 315 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 316 case SND_SOC_DAIFMT_NB_NF: 317 control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; 318 break; 319 case SND_SOC_DAIFMT_NB_IF: 320 control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; 321 control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 322 break; 323 case SND_SOC_DAIFMT_IB_NF: 324 break; 325 case SND_SOC_DAIFMT_IB_IF: 326 control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 327 break; 328 default: 329 return -EINVAL; 330 } 331 332 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 333 case SND_SOC_DAIFMT_I2S: 334 chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; 335 break; 336 case SND_SOC_DAIFMT_LEFT_J: 337 break; 338 default: 339 return -EINVAL; 340 } 341 342 control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK | 343 IMG_I2S_OUT_CTL_MASTER_MASK | 344 IMG_I2S_OUT_CTL_BCLK_POL_MASK | 345 IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 346 347 chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; 348 349 ret = pm_runtime_resume_and_get(i2s->dev); 350 if (ret < 0) 351 return ret; 352 353 img_i2s_out_disable(i2s); 354 355 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 356 reg = (reg & ~control_mask) | control_set; 357 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 358 359 for (i = 0; i < i2s->active_channels; i++) 360 img_i2s_out_ch_disable(i2s, i); 361 362 for (i = 0; i < i2s->max_i2s_chan; i++) { 363 reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 364 reg = (reg & ~chan_control_mask) | chan_control_set; 365 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 366 } 367 368 for (i = 0; i < i2s->active_channels; i++) 369 img_i2s_out_ch_enable(i2s, i); 370 371 img_i2s_out_enable(i2s); 372 pm_runtime_put(i2s->dev); 373 374 i2s->force_clk_active = force_clk_active; 375 376 return 0; 377 } 378 379 static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) 380 { 381 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 382 383 snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); 384 385 return 0; 386 } 387 388 static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { 389 .probe = img_i2s_out_dai_probe, 390 .trigger = img_i2s_out_trigger, 391 .hw_params = img_i2s_out_hw_params, 392 .set_fmt = img_i2s_out_set_fmt 393 }; 394 395 static const struct snd_soc_component_driver img_i2s_out_component = { 396 .name = "img-i2s-out", 397 .legacy_dai_naming = 1, 398 }; 399 400 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, 401 struct snd_pcm_hw_params *params, struct dma_slave_config *sc) 402 { 403 unsigned int i2s_channels = params_channels(params) / 2; 404 struct snd_soc_pcm_runtime *rtd = st->private_data; 405 struct snd_dmaengine_dai_dma_data *dma_data; 406 int ret; 407 408 dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), st); 409 410 ret = snd_hwparams_to_dma_slave_config(st, params, sc); 411 if (ret) 412 return ret; 413 414 sc->dst_addr = dma_data->addr; 415 sc->dst_addr_width = dma_data->addr_width; 416 sc->dst_maxburst = 4 * i2s_channels; 417 418 return 0; 419 } 420 421 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { 422 .prepare_slave_config = img_i2s_out_dma_prepare_slave_config 423 }; 424 425 static int img_i2s_out_probe(struct platform_device *pdev) 426 { 427 struct img_i2s_out *i2s; 428 struct resource *res; 429 void __iomem *base; 430 int i, ret; 431 unsigned int max_i2s_chan_pow_2; 432 u32 reg; 433 struct device *dev = &pdev->dev; 434 435 i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); 436 if (!i2s) 437 return -ENOMEM; 438 439 platform_set_drvdata(pdev, i2s); 440 441 i2s->dev = &pdev->dev; 442 443 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 444 if (IS_ERR(base)) 445 return PTR_ERR(base); 446 447 i2s->base = base; 448 449 if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", 450 &i2s->max_i2s_chan)) { 451 dev_err(&pdev->dev, "No img,i2s-channels property\n"); 452 return -EINVAL; 453 } 454 455 max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); 456 457 i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); 458 459 i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); 460 if (IS_ERR(i2s->rst)) 461 return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), 462 "No top level reset found\n"); 463 464 i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); 465 if (IS_ERR(i2s->clk_sys)) 466 return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), 467 "Failed to acquire clock 'sys'\n"); 468 469 i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); 470 if (IS_ERR(i2s->clk_ref)) 471 return dev_err_probe(dev, PTR_ERR(i2s->clk_ref), 472 "Failed to acquire clock 'ref'\n"); 473 474 i2s->suspend_ch_ctl = devm_kcalloc(dev, 475 i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); 476 if (!i2s->suspend_ch_ctl) 477 return -ENOMEM; 478 479 pm_runtime_enable(&pdev->dev); 480 if (!pm_runtime_enabled(&pdev->dev)) { 481 ret = img_i2s_out_runtime_resume(&pdev->dev); 482 if (ret) 483 goto err_pm_disable; 484 } 485 ret = pm_runtime_resume_and_get(&pdev->dev); 486 if (ret < 0) 487 goto err_suspend; 488 489 reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; 490 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 491 492 reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | 493 IMG_I2S_OUT_CHAN_CTL_LT_MASK | 494 IMG_I2S_OUT_CHAN_CTL_CH_MASK | 495 (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); 496 497 for (i = 0; i < i2s->max_i2s_chan; i++) 498 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 499 500 img_i2s_out_reset(i2s); 501 pm_runtime_put(&pdev->dev); 502 503 i2s->active_channels = 1; 504 i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; 505 i2s->dma_data.addr_width = 4; 506 i2s->dma_data.maxburst = 4; 507 508 i2s->dai_driver.playback.channels_min = 2; 509 i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; 510 i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; 511 i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; 512 i2s->dai_driver.ops = &img_i2s_out_dai_ops; 513 514 ret = devm_snd_soc_register_component(&pdev->dev, 515 &img_i2s_out_component, &i2s->dai_driver, 1); 516 if (ret) 517 goto err_suspend; 518 519 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 520 &img_i2s_out_dma_config, 0); 521 if (ret) 522 goto err_suspend; 523 524 return 0; 525 526 err_suspend: 527 if (!pm_runtime_status_suspended(&pdev->dev)) 528 img_i2s_out_runtime_suspend(&pdev->dev); 529 err_pm_disable: 530 pm_runtime_disable(&pdev->dev); 531 532 return ret; 533 } 534 535 static void img_i2s_out_dev_remove(struct platform_device *pdev) 536 { 537 pm_runtime_disable(&pdev->dev); 538 if (!pm_runtime_status_suspended(&pdev->dev)) 539 img_i2s_out_runtime_suspend(&pdev->dev); 540 } 541 542 #ifdef CONFIG_PM_SLEEP 543 static int img_i2s_out_suspend(struct device *dev) 544 { 545 struct img_i2s_out *i2s = dev_get_drvdata(dev); 546 int i, ret; 547 u32 reg; 548 549 if (pm_runtime_status_suspended(dev)) { 550 ret = img_i2s_out_runtime_resume(dev); 551 if (ret) 552 return ret; 553 } 554 555 for (i = 0; i < i2s->max_i2s_chan; i++) { 556 reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 557 i2s->suspend_ch_ctl[i] = reg; 558 } 559 560 i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 561 562 img_i2s_out_runtime_suspend(dev); 563 564 return 0; 565 } 566 567 static int img_i2s_out_resume(struct device *dev) 568 { 569 struct img_i2s_out *i2s = dev_get_drvdata(dev); 570 int i, ret; 571 u32 reg; 572 573 ret = img_i2s_out_runtime_resume(dev); 574 if (ret) 575 return ret; 576 577 for (i = 0; i < i2s->max_i2s_chan; i++) { 578 reg = i2s->suspend_ch_ctl[i]; 579 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 580 } 581 582 img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); 583 584 if (pm_runtime_status_suspended(dev)) 585 img_i2s_out_runtime_suspend(dev); 586 587 return 0; 588 } 589 #endif 590 591 static const struct of_device_id img_i2s_out_of_match[] = { 592 { .compatible = "img,i2s-out" }, 593 {} 594 }; 595 MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); 596 597 static const struct dev_pm_ops img_i2s_out_pm_ops = { 598 SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, 599 img_i2s_out_runtime_resume, NULL) 600 SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) 601 }; 602 603 static struct platform_driver img_i2s_out_driver = { 604 .driver = { 605 .name = "img-i2s-out", 606 .of_match_table = img_i2s_out_of_match, 607 .pm = &img_i2s_out_pm_ops 608 }, 609 .probe = img_i2s_out_probe, 610 .remove_new = img_i2s_out_dev_remove 611 }; 612 module_platform_driver(img_i2s_out_driver); 613 614 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 615 MODULE_DESCRIPTION("IMG I2S Output Driver"); 616 MODULE_LICENSE("GPL v2"); 617