img-i2s-out.c (15828B)
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 51struct 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 67static 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 77static 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 98static 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 104static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg) 105{ 106 return readl(i2s->base + reg); 107} 108 109static 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 115static 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 121static 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 130static 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 139static 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 148static 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 157static 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 185static 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 213static 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 291static 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 pm_runtime_put_noidle(i2s->dev); 352 return ret; 353 } 354 355 img_i2s_out_disable(i2s); 356 357 reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 358 reg = (reg & ~control_mask) | control_set; 359 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 360 361 for (i = 0; i < i2s->active_channels; i++) 362 img_i2s_out_ch_disable(i2s, i); 363 364 for (i = 0; i < i2s->max_i2s_chan; i++) { 365 reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 366 reg = (reg & ~chan_control_mask) | chan_control_set; 367 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 368 } 369 370 for (i = 0; i < i2s->active_channels; i++) 371 img_i2s_out_ch_enable(i2s, i); 372 373 img_i2s_out_enable(i2s); 374 pm_runtime_put(i2s->dev); 375 376 i2s->force_clk_active = force_clk_active; 377 378 return 0; 379} 380 381static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { 382 .trigger = img_i2s_out_trigger, 383 .hw_params = img_i2s_out_hw_params, 384 .set_fmt = img_i2s_out_set_fmt 385}; 386 387static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) 388{ 389 struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 390 391 snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); 392 393 return 0; 394} 395 396static const struct snd_soc_component_driver img_i2s_out_component = { 397 .name = "img-i2s-out" 398}; 399 400static 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(asoc_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 421static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { 422 .prepare_slave_config = img_i2s_out_dma_prepare_slave_config 423}; 424 425static 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_get_sync(&pdev->dev); 486 if (ret < 0) { 487 pm_runtime_put_noidle(&pdev->dev); 488 goto err_suspend; 489 } 490 491 reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; 492 img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 493 494 reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | 495 IMG_I2S_OUT_CHAN_CTL_LT_MASK | 496 IMG_I2S_OUT_CHAN_CTL_CH_MASK | 497 (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); 498 499 for (i = 0; i < i2s->max_i2s_chan; i++) 500 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 501 502 img_i2s_out_reset(i2s); 503 pm_runtime_put(&pdev->dev); 504 505 i2s->active_channels = 1; 506 i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; 507 i2s->dma_data.addr_width = 4; 508 i2s->dma_data.maxburst = 4; 509 510 i2s->dai_driver.probe = img_i2s_out_dai_probe; 511 i2s->dai_driver.playback.channels_min = 2; 512 i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; 513 i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; 514 i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; 515 i2s->dai_driver.ops = &img_i2s_out_dai_ops; 516 517 ret = devm_snd_soc_register_component(&pdev->dev, 518 &img_i2s_out_component, &i2s->dai_driver, 1); 519 if (ret) 520 goto err_suspend; 521 522 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 523 &img_i2s_out_dma_config, 0); 524 if (ret) 525 goto err_suspend; 526 527 return 0; 528 529err_suspend: 530 if (!pm_runtime_status_suspended(&pdev->dev)) 531 img_i2s_out_runtime_suspend(&pdev->dev); 532err_pm_disable: 533 pm_runtime_disable(&pdev->dev); 534 535 return ret; 536} 537 538static int img_i2s_out_dev_remove(struct platform_device *pdev) 539{ 540 pm_runtime_disable(&pdev->dev); 541 if (!pm_runtime_status_suspended(&pdev->dev)) 542 img_i2s_out_runtime_suspend(&pdev->dev); 543 544 return 0; 545} 546 547#ifdef CONFIG_PM_SLEEP 548static int img_i2s_out_suspend(struct device *dev) 549{ 550 struct img_i2s_out *i2s = dev_get_drvdata(dev); 551 int i, ret; 552 u32 reg; 553 554 if (pm_runtime_status_suspended(dev)) { 555 ret = img_i2s_out_runtime_resume(dev); 556 if (ret) 557 return ret; 558 } 559 560 for (i = 0; i < i2s->max_i2s_chan; i++) { 561 reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 562 i2s->suspend_ch_ctl[i] = reg; 563 } 564 565 i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 566 567 img_i2s_out_runtime_suspend(dev); 568 569 return 0; 570} 571 572static int img_i2s_out_resume(struct device *dev) 573{ 574 struct img_i2s_out *i2s = dev_get_drvdata(dev); 575 int i, ret; 576 u32 reg; 577 578 ret = img_i2s_out_runtime_resume(dev); 579 if (ret) 580 return ret; 581 582 for (i = 0; i < i2s->max_i2s_chan; i++) { 583 reg = i2s->suspend_ch_ctl[i]; 584 img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 585 } 586 587 img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); 588 589 if (pm_runtime_status_suspended(dev)) 590 img_i2s_out_runtime_suspend(dev); 591 592 return 0; 593} 594#endif 595 596static const struct of_device_id img_i2s_out_of_match[] = { 597 { .compatible = "img,i2s-out" }, 598 {} 599}; 600MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); 601 602static const struct dev_pm_ops img_i2s_out_pm_ops = { 603 SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, 604 img_i2s_out_runtime_resume, NULL) 605 SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) 606}; 607 608static struct platform_driver img_i2s_out_driver = { 609 .driver = { 610 .name = "img-i2s-out", 611 .of_match_table = img_i2s_out_of_match, 612 .pm = &img_i2s_out_pm_ops 613 }, 614 .probe = img_i2s_out_probe, 615 .remove = img_i2s_out_dev_remove 616}; 617module_platform_driver(img_i2s_out_driver); 618 619MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 620MODULE_DESCRIPTION("IMG I2S Output Driver"); 621MODULE_LICENSE("GPL v2");