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_MASTER_MASK) { 306 case SND_SOC_DAIFMT_CBM_CFM: 307 break; 308 case SND_SOC_DAIFMT_CBS_CFS: 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_get_sync(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 const struct snd_soc_dai_ops img_i2s_out_dai_ops = { 380 .trigger = img_i2s_out_trigger, 381 .hw_params = img_i2s_out_hw_params, 382 .set_fmt = img_i2s_out_set_fmt 383 }; 384 385 static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) 386 { 387 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 388 389 snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); 390 391 return 0; 392 } 393 394 static const struct snd_soc_component_driver img_i2s_out_component = { 395 .name = "img-i2s-out" 396 }; 397 398 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, 399 struct snd_pcm_hw_params *params, struct dma_slave_config *sc) 400 { 401 unsigned int i2s_channels = params_channels(params) / 2; 402 struct snd_soc_pcm_runtime *rtd = st->private_data; 403 struct snd_dmaengine_dai_dma_data *dma_data; 404 int ret; 405 406 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); 407 408 ret = snd_hwparams_to_dma_slave_config(st, params, sc); 409 if (ret) 410 return ret; 411 412 sc->dst_addr = dma_data->addr; 413 sc->dst_addr_width = dma_data->addr_width; 414 sc->dst_maxburst = 4 * i2s_channels; 415 416 return 0; 417 } 418 419 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { 420 .prepare_slave_config = img_i2s_out_dma_prepare_slave_config 421 }; 422 423 static int img_i2s_out_probe(struct platform_device *pdev) 424 { 425 struct img_i2s_out *i2s; 426 struct resource *res; 427 void __iomem *base; 428 int i, ret; 429 unsigned int max_i2s_chan_pow_2; 430 u32 reg; 431 struct device *dev = &pdev->dev; 432 433 i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); 434 if (!i2s) 435 return -ENOMEM; 436 437 platform_set_drvdata(pdev, i2s); 438 439 i2s->dev = &pdev->dev; 440 441 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 442 base = devm_ioremap_resource(&pdev->dev, res); 443 if (IS_ERR(base)) 444 return PTR_ERR(base); 445 446 i2s->base = base; 447 448 if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", 449 &i2s->max_i2s_chan)) { 450 dev_err(&pdev->dev, "No img,i2s-channels property\n"); 451 return -EINVAL; 452 } 453 454 max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); 455 456 i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); 457 458 i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); 459 if (IS_ERR(i2s->rst)) { 460 if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) 461 dev_err(&pdev->dev, "No top level reset found\n"); 462 return PTR_ERR(i2s->rst); 463 } 464 465 i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); 466 if (IS_ERR(i2s->clk_sys)) { 467 if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) 468 dev_err(dev, "Failed to acquire clock 'sys'\n"); 469 return PTR_ERR(i2s->clk_sys); 470 } 471 472 i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); 473 if (IS_ERR(i2s->clk_ref)) { 474 if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) 475 dev_err(dev, "Failed to acquire clock 'ref'\n"); 476 return PTR_ERR(i2s->clk_ref); 477 } 478 479 i2s->suspend_ch_ctl = devm_kcalloc(dev, 480 i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); 481 if (!i2s->suspend_ch_ctl) 482 return -ENOMEM; 483 484 pm_runtime_enable(&pdev->dev); 485 if (!pm_runtime_enabled(&pdev->dev)) { 486 ret = img_i2s_out_runtime_resume(&pdev->dev); 487 if (ret) 488 goto err_pm_disable; 489 } 490 ret = pm_runtime_get_sync(&pdev->dev); 491 if (ret < 0) 492 goto err_suspend; 493 494 reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; 495 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 496 497 reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | 498 IMG_I2S_OUT_CHAN_CTL_LT_MASK | 499 IMG_I2S_OUT_CHAN_CTL_CH_MASK | 500 (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); 501 502 for (i = 0; i < i2s->max_i2s_chan; i++) 503 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 504 505 img_i2s_out_reset(i2s); 506 pm_runtime_put(&pdev->dev); 507 508 i2s->active_channels = 1; 509 i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; 510 i2s->dma_data.addr_width = 4; 511 i2s->dma_data.maxburst = 4; 512 513 i2s->dai_driver.probe = img_i2s_out_dai_probe; 514 i2s->dai_driver.playback.channels_min = 2; 515 i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; 516 i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; 517 i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; 518 i2s->dai_driver.ops = &img_i2s_out_dai_ops; 519 520 ret = devm_snd_soc_register_component(&pdev->dev, 521 &img_i2s_out_component, &i2s->dai_driver, 1); 522 if (ret) 523 goto err_suspend; 524 525 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 526 &img_i2s_out_dma_config, 0); 527 if (ret) 528 goto err_suspend; 529 530 return 0; 531 532 err_suspend: 533 if (!pm_runtime_status_suspended(&pdev->dev)) 534 img_i2s_out_runtime_suspend(&pdev->dev); 535 err_pm_disable: 536 pm_runtime_disable(&pdev->dev); 537 538 return ret; 539 } 540 541 static int img_i2s_out_dev_remove(struct platform_device *pdev) 542 { 543 pm_runtime_disable(&pdev->dev); 544 if (!pm_runtime_status_suspended(&pdev->dev)) 545 img_i2s_out_runtime_suspend(&pdev->dev); 546 547 return 0; 548 } 549 550 #ifdef CONFIG_PM_SLEEP 551 static int img_i2s_out_suspend(struct device *dev) 552 { 553 struct img_i2s_out *i2s = dev_get_drvdata(dev); 554 int i, ret; 555 u32 reg; 556 557 if (pm_runtime_status_suspended(dev)) { 558 ret = img_i2s_out_runtime_resume(dev); 559 if (ret) 560 return ret; 561 } 562 563 for (i = 0; i < i2s->max_i2s_chan; i++) { 564 reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 565 i2s->suspend_ch_ctl[i] = reg; 566 } 567 568 i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 569 570 img_i2s_out_runtime_suspend(dev); 571 572 return 0; 573 } 574 575 static int img_i2s_out_resume(struct device *dev) 576 { 577 struct img_i2s_out *i2s = dev_get_drvdata(dev); 578 int i, ret; 579 u32 reg; 580 581 ret = img_i2s_out_runtime_resume(dev); 582 if (ret) 583 return ret; 584 585 for (i = 0; i < i2s->max_i2s_chan; i++) { 586 reg = i2s->suspend_ch_ctl[i]; 587 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 588 } 589 590 img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); 591 592 if (pm_runtime_status_suspended(dev)) 593 img_i2s_out_runtime_suspend(dev); 594 595 return 0; 596 } 597 #endif 598 599 static const struct of_device_id img_i2s_out_of_match[] = { 600 { .compatible = "img,i2s-out" }, 601 {} 602 }; 603 MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); 604 605 static const struct dev_pm_ops img_i2s_out_pm_ops = { 606 SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, 607 img_i2s_out_runtime_resume, NULL) 608 SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) 609 }; 610 611 static struct platform_driver img_i2s_out_driver = { 612 .driver = { 613 .name = "img-i2s-out", 614 .of_match_table = img_i2s_out_of_match, 615 .pm = &img_i2s_out_pm_ops 616 }, 617 .probe = img_i2s_out_probe, 618 .remove = img_i2s_out_dev_remove 619 }; 620 module_platform_driver(img_i2s_out_driver); 621 622 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 623 MODULE_DESCRIPTION("IMG I2S Output Driver"); 624 MODULE_LICENSE("GPL v2"); 625