18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IMG I2S output controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Damien Horsley <Damien.Horsley@imgtec.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/reset.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 218c2ecf20Sopenharmony_ci#include <sound/initval.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 248c2ecf20Sopenharmony_ci#include <sound/soc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_TX_FIFO 0x0 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL 0x4 298c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24) 308c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000 318c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13 328c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8) 338c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6) 348c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5) 358c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4) 368c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3) 378c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2) 388c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CTL_ME_MASK BIT(0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CH_CTL 0x4 418c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11) 428c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10) 438c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0 448c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4 458c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3) 468c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1) 478c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define IMG_I2S_OUT_CH_STRIDE 0x20 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct img_i2s_out { 528c2ecf20Sopenharmony_ci void __iomem *base; 538c2ecf20Sopenharmony_ci struct clk *clk_sys; 548c2ecf20Sopenharmony_ci struct clk *clk_ref; 558c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_data; 568c2ecf20Sopenharmony_ci struct device *dev; 578c2ecf20Sopenharmony_ci unsigned int max_i2s_chan; 588c2ecf20Sopenharmony_ci void __iomem *channel_base; 598c2ecf20Sopenharmony_ci bool force_clk_active; 608c2ecf20Sopenharmony_ci unsigned int active_channels; 618c2ecf20Sopenharmony_ci struct reset_control *rst; 628c2ecf20Sopenharmony_ci struct snd_soc_dai_driver dai_driver; 638c2ecf20Sopenharmony_ci u32 suspend_ctl; 648c2ecf20Sopenharmony_ci u32 *suspend_ch_ctl; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int img_i2s_out_runtime_suspend(struct device *dev) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = dev_get_drvdata(dev); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_ref); 728c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_sys); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int img_i2s_out_runtime_resume(struct device *dev) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = dev_get_drvdata(dev); 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_sys); 838c2ecf20Sopenharmony_ci if (ret) { 848c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_ref); 898c2ecf20Sopenharmony_ci if (ret) { 908c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 918c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_sys); 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val, 998c2ecf20Sopenharmony_ci u32 reg) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci writel(val, i2s->base + reg); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci return readl(i2s->base + reg); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s, 1108c2ecf20Sopenharmony_ci u32 chan, u32 val, u32 reg) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan, 1168c2ecf20Sopenharmony_ci u32 reg) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u32 reg; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); 1268c2ecf20Sopenharmony_ci reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; 1278c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u32 reg; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); 1358c2ecf20Sopenharmony_ci reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK; 1368c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic inline void img_i2s_out_disable(struct img_i2s_out *i2s) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci u32 reg; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 1448c2ecf20Sopenharmony_ci reg &= ~IMG_I2S_OUT_CTL_ME_MASK; 1458c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic inline void img_i2s_out_enable(struct img_i2s_out *i2s) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci u32 reg; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 1538c2ecf20Sopenharmony_ci reg |= IMG_I2S_OUT_CTL_ME_MASK; 1548c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void img_i2s_out_reset(struct img_i2s_out *i2s) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int i; 1608c2ecf20Sopenharmony_ci u32 core_ctl, chan_ctl; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) & 1638c2ecf20Sopenharmony_ci ~IMG_I2S_OUT_CTL_ME_MASK & 1648c2ecf20Sopenharmony_ci ~IMG_I2S_OUT_CTL_DATA_EN_MASK; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!i2s->force_clk_active) 1678c2ecf20Sopenharmony_ci core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) & 1708c2ecf20Sopenharmony_ci ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci reset_control_assert(i2s->rst); 1738c2ecf20Sopenharmony_ci reset_control_deassert(i2s->rst); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci for (i = 0; i < i2s->max_i2s_chan; i++) 1768c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci for (i = 0; i < i2s->active_channels; i++) 1798c2ecf20Sopenharmony_ci img_i2s_out_ch_enable(i2s, i); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL); 1828c2ecf20Sopenharmony_ci img_i2s_out_enable(i2s); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd, 1868c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 1898c2ecf20Sopenharmony_ci u32 reg; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci switch (cmd) { 1928c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1938c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 1948c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1958c2ecf20Sopenharmony_ci reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 1968c2ecf20Sopenharmony_ci if (!i2s->force_clk_active) 1978c2ecf20Sopenharmony_ci reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK; 1988c2ecf20Sopenharmony_ci reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK; 1998c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2028c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2038c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2048c2ecf20Sopenharmony_ci img_i2s_out_reset(i2s); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int img_i2s_out_hw_params(struct snd_pcm_substream *substream, 2148c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 2178c2ecf20Sopenharmony_ci unsigned int channels, i2s_channels; 2188c2ecf20Sopenharmony_ci long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; 2198c2ecf20Sopenharmony_ci int i; 2208c2ecf20Sopenharmony_ci u32 reg, control_mask, control_set = 0; 2218c2ecf20Sopenharmony_ci snd_pcm_format_t format; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci rate = params_rate(params); 2248c2ecf20Sopenharmony_ci format = params_format(params); 2258c2ecf20Sopenharmony_ci channels = params_channels(params); 2268c2ecf20Sopenharmony_ci i2s_channels = channels / 2; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (format != SNDRV_PCM_FORMAT_S32_LE) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if ((channels < 2) || 2328c2ecf20Sopenharmony_ci (channels > (i2s->max_i2s_chan * 2)) || 2338c2ecf20Sopenharmony_ci (channels % 2)) 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256); 2378c2ecf20Sopenharmony_ci if (pre_div_a < 0) 2388c2ecf20Sopenharmony_ci return pre_div_a; 2398c2ecf20Sopenharmony_ci pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384); 2408c2ecf20Sopenharmony_ci if (pre_div_b < 0) 2418c2ecf20Sopenharmony_ci return pre_div_b; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci diff_a = abs((pre_div_a / 256) - rate); 2448c2ecf20Sopenharmony_ci diff_b = abs((pre_div_b / 384) - rate); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* If diffs are equal, use lower clock rate */ 2478c2ecf20Sopenharmony_ci if (diff_a > diff_b) 2488c2ecf20Sopenharmony_ci clk_set_rate(i2s->clk_ref, pre_div_b); 2498c2ecf20Sopenharmony_ci else 2508c2ecf20Sopenharmony_ci clk_set_rate(i2s->clk_ref, pre_div_a); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Another driver (eg alsa machine driver) may have rejected the above 2548c2ecf20Sopenharmony_ci * change. Get the current rate and set the register bit according to 2558c2ecf20Sopenharmony_ci * the new minimum diff 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(i2s->clk_ref); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci diff_a = abs((clk_rate / 256) - rate); 2608c2ecf20Sopenharmony_ci diff_b = abs((clk_rate / 384) - rate); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (diff_a > diff_b) 2638c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_CLK_MASK; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci control_set |= ((i2s_channels - 1) << 2668c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) & 2678c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci control_mask = IMG_I2S_OUT_CTL_CLK_MASK | 2708c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci img_i2s_out_disable(i2s); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 2758c2ecf20Sopenharmony_ci reg = (reg & ~control_mask) | control_set; 2768c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci for (i = 0; i < i2s_channels; i++) 2798c2ecf20Sopenharmony_ci img_i2s_out_ch_enable(i2s, i); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci for (; i < i2s->max_i2s_chan; i++) 2828c2ecf20Sopenharmony_ci img_i2s_out_ch_disable(i2s, i); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci img_i2s_out_enable(i2s); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci i2s->active_channels = i2s_channels; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 2948c2ecf20Sopenharmony_ci int i, ret; 2958c2ecf20Sopenharmony_ci bool force_clk_active; 2968c2ecf20Sopenharmony_ci u32 chan_control_mask, control_mask, chan_control_set = 0; 2978c2ecf20Sopenharmony_ci u32 reg, control_set = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) == 3008c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CONT); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (force_clk_active) 3038c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 3098c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_MASTER_MASK; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 3168c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 3178c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 3208c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; 3218c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 3268c2ecf20Sopenharmony_ci control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci default: 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3338c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 3348c2ecf20Sopenharmony_ci chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci return -EINVAL; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK | 3438c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_MASTER_MASK | 3448c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_BCLK_POL_MASK | 3458c2ecf20Sopenharmony_ci IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(i2s->dev); 3508c2ecf20Sopenharmony_ci if (ret < 0) { 3518c2ecf20Sopenharmony_ci pm_runtime_put_noidle(i2s->dev); 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci img_i2s_out_disable(i2s); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 3588c2ecf20Sopenharmony_ci reg = (reg & ~control_mask) | control_set; 3598c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (i = 0; i < i2s->active_channels; i++) 3628c2ecf20Sopenharmony_ci img_i2s_out_ch_disable(i2s, i); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (i = 0; i < i2s->max_i2s_chan; i++) { 3658c2ecf20Sopenharmony_ci reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 3668c2ecf20Sopenharmony_ci reg = (reg & ~chan_control_mask) | chan_control_set; 3678c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci for (i = 0; i < i2s->active_channels; i++) 3718c2ecf20Sopenharmony_ci img_i2s_out_ch_enable(i2s, i); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci img_i2s_out_enable(i2s); 3748c2ecf20Sopenharmony_ci pm_runtime_put(i2s->dev); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci i2s->force_clk_active = force_clk_active; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops img_i2s_out_dai_ops = { 3828c2ecf20Sopenharmony_ci .trigger = img_i2s_out_trigger, 3838c2ecf20Sopenharmony_ci .hw_params = img_i2s_out_hw_params, 3848c2ecf20Sopenharmony_ci .set_fmt = img_i2s_out_set_fmt 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int img_i2s_out_dai_probe(struct snd_soc_dai *dai) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver img_i2s_out_component = { 3978c2ecf20Sopenharmony_ci .name = "img-i2s-out" 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, 4018c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, struct dma_slave_config *sc) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci unsigned int i2s_channels = params_channels(params) / 2; 4048c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = st->private_data; 4058c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci ret = snd_hwparams_to_dma_slave_config(st, params, sc); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci sc->dst_addr = dma_data->addr; 4158c2ecf20Sopenharmony_ci sc->dst_addr_width = dma_data->addr_width; 4168c2ecf20Sopenharmony_ci sc->dst_maxburst = 4 * i2s_channels; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { 4228c2ecf20Sopenharmony_ci .prepare_slave_config = img_i2s_out_dma_prepare_slave_config 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int img_i2s_out_probe(struct platform_device *pdev) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct img_i2s_out *i2s; 4288c2ecf20Sopenharmony_ci struct resource *res; 4298c2ecf20Sopenharmony_ci void __iomem *base; 4308c2ecf20Sopenharmony_ci int i, ret; 4318c2ecf20Sopenharmony_ci unsigned int max_i2s_chan_pow_2; 4328c2ecf20Sopenharmony_ci u32 reg; 4338c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); 4368c2ecf20Sopenharmony_ci if (!i2s) 4378c2ecf20Sopenharmony_ci return -ENOMEM; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, i2s); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci i2s->dev = &pdev->dev; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4448c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 4458c2ecf20Sopenharmony_ci if (IS_ERR(base)) 4468c2ecf20Sopenharmony_ci return PTR_ERR(base); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci i2s->base = base; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", 4518c2ecf20Sopenharmony_ci &i2s->max_i2s_chan)) { 4528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No img,i2s-channels property\n"); 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); 4618c2ecf20Sopenharmony_ci if (IS_ERR(i2s->rst)) { 4628c2ecf20Sopenharmony_ci if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) 4638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No top level reset found\n"); 4648c2ecf20Sopenharmony_ci return PTR_ERR(i2s->rst); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); 4688c2ecf20Sopenharmony_ci if (IS_ERR(i2s->clk_sys)) { 4698c2ecf20Sopenharmony_ci if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) 4708c2ecf20Sopenharmony_ci dev_err(dev, "Failed to acquire clock 'sys'\n"); 4718c2ecf20Sopenharmony_ci return PTR_ERR(i2s->clk_sys); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); 4758c2ecf20Sopenharmony_ci if (IS_ERR(i2s->clk_ref)) { 4768c2ecf20Sopenharmony_ci if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) 4778c2ecf20Sopenharmony_ci dev_err(dev, "Failed to acquire clock 'ref'\n"); 4788c2ecf20Sopenharmony_ci return PTR_ERR(i2s->clk_ref); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci i2s->suspend_ch_ctl = devm_kcalloc(dev, 4828c2ecf20Sopenharmony_ci i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (!i2s->suspend_ch_ctl) 4848c2ecf20Sopenharmony_ci return -ENOMEM; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4878c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 4888c2ecf20Sopenharmony_ci ret = img_i2s_out_runtime_resume(&pdev->dev); 4898c2ecf20Sopenharmony_ci if (ret) 4908c2ecf20Sopenharmony_ci goto err_pm_disable; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 4938c2ecf20Sopenharmony_ci if (ret < 0) { 4948c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 4958c2ecf20Sopenharmony_ci goto err_suspend; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; 4998c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | 5028c2ecf20Sopenharmony_ci IMG_I2S_OUT_CHAN_CTL_LT_MASK | 5038c2ecf20Sopenharmony_ci IMG_I2S_OUT_CHAN_CTL_CH_MASK | 5048c2ecf20Sopenharmony_ci (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci for (i = 0; i < i2s->max_i2s_chan; i++) 5078c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci img_i2s_out_reset(i2s); 5108c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci i2s->active_channels = 1; 5138c2ecf20Sopenharmony_ci i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; 5148c2ecf20Sopenharmony_ci i2s->dma_data.addr_width = 4; 5158c2ecf20Sopenharmony_ci i2s->dma_data.maxburst = 4; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci i2s->dai_driver.probe = img_i2s_out_dai_probe; 5188c2ecf20Sopenharmony_ci i2s->dai_driver.playback.channels_min = 2; 5198c2ecf20Sopenharmony_ci i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; 5208c2ecf20Sopenharmony_ci i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; 5218c2ecf20Sopenharmony_ci i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; 5228c2ecf20Sopenharmony_ci i2s->dai_driver.ops = &img_i2s_out_dai_ops; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 5258c2ecf20Sopenharmony_ci &img_i2s_out_component, &i2s->dai_driver, 1); 5268c2ecf20Sopenharmony_ci if (ret) 5278c2ecf20Sopenharmony_ci goto err_suspend; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 5308c2ecf20Sopenharmony_ci &img_i2s_out_dma_config, 0); 5318c2ecf20Sopenharmony_ci if (ret) 5328c2ecf20Sopenharmony_ci goto err_suspend; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cierr_suspend: 5378c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5388c2ecf20Sopenharmony_ci img_i2s_out_runtime_suspend(&pdev->dev); 5398c2ecf20Sopenharmony_cierr_pm_disable: 5408c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int img_i2s_out_dev_remove(struct platform_device *pdev) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5488c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5498c2ecf20Sopenharmony_ci img_i2s_out_runtime_suspend(&pdev->dev); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5558c2ecf20Sopenharmony_cistatic int img_i2s_out_suspend(struct device *dev) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = dev_get_drvdata(dev); 5588c2ecf20Sopenharmony_ci int i, ret; 5598c2ecf20Sopenharmony_ci u32 reg; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (pm_runtime_status_suspended(dev)) { 5628c2ecf20Sopenharmony_ci ret = img_i2s_out_runtime_resume(dev); 5638c2ecf20Sopenharmony_ci if (ret) 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci for (i = 0; i < i2s->max_i2s_chan; i++) { 5688c2ecf20Sopenharmony_ci reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); 5698c2ecf20Sopenharmony_ci i2s->suspend_ch_ctl[i] = reg; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci img_i2s_out_runtime_suspend(dev); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int img_i2s_out_resume(struct device *dev) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct img_i2s_out *i2s = dev_get_drvdata(dev); 5828c2ecf20Sopenharmony_ci int i, ret; 5838c2ecf20Sopenharmony_ci u32 reg; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ret = img_i2s_out_runtime_resume(dev); 5868c2ecf20Sopenharmony_ci if (ret) 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci for (i = 0; i < i2s->max_i2s_chan; i++) { 5908c2ecf20Sopenharmony_ci reg = i2s->suspend_ch_ctl[i]; 5918c2ecf20Sopenharmony_ci img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (pm_runtime_status_suspended(dev)) 5978c2ecf20Sopenharmony_ci img_i2s_out_runtime_suspend(dev); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci#endif 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic const struct of_device_id img_i2s_out_of_match[] = { 6048c2ecf20Sopenharmony_ci { .compatible = "img,i2s-out" }, 6058c2ecf20Sopenharmony_ci {} 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, img_i2s_out_of_match); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic const struct dev_pm_ops img_i2s_out_pm_ops = { 6108c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, 6118c2ecf20Sopenharmony_ci img_i2s_out_runtime_resume, NULL) 6128c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) 6138c2ecf20Sopenharmony_ci}; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic struct platform_driver img_i2s_out_driver = { 6168c2ecf20Sopenharmony_ci .driver = { 6178c2ecf20Sopenharmony_ci .name = "img-i2s-out", 6188c2ecf20Sopenharmony_ci .of_match_table = img_i2s_out_of_match, 6198c2ecf20Sopenharmony_ci .pm = &img_i2s_out_pm_ops 6208c2ecf20Sopenharmony_ci }, 6218c2ecf20Sopenharmony_ci .probe = img_i2s_out_probe, 6228c2ecf20Sopenharmony_ci .remove = img_i2s_out_dev_remove 6238c2ecf20Sopenharmony_ci}; 6248c2ecf20Sopenharmony_cimodule_platform_driver(img_i2s_out_driver); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 6278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IMG I2S Output Driver"); 6288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 629