1 // SPDX-License-Identifier: GPL-2.0
3 // Xilinx ASoC I2S audio support
5 // Copyright (C) 2018 Xilinx, Inc.
7 // Author: Praveen Vuppala <praveenv@xilinx.com>
8 // Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
11 #include <linux/module.h>
13 #include <linux/of_platform.h>
14 #include <linux/platform_device.h>
15 #include <sound/pcm_params.h>
16 #include <sound/soc.h>
18 #define DRV_NAME "xlnx_i2s"
20 #define I2S_CORE_CTRL_OFFSET 0x08
21 #define I2S_I2STIM_OFFSET 0x20
22 #define I2S_CH0_OFFSET 0x30
23 #define I2S_I2STIM_VALID_MASK GENMASK(7, 0)
25 static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai
*cpu_dai
,
28 void __iomem
*base
= snd_soc_dai_get_drvdata(cpu_dai
);
30 if (!div
|| (div
& ~I2S_I2STIM_VALID_MASK
))
33 writel(div
, base
+ I2S_I2STIM_OFFSET
);
38 static int xlnx_i2s_hw_params(struct snd_pcm_substream
*substream
,
39 struct snd_pcm_hw_params
*params
,
40 struct snd_soc_dai
*i2s_dai
)
43 void __iomem
*base
= snd_soc_dai_get_drvdata(i2s_dai
);
45 chan_id
= params_channels(params
) / 2;
48 reg_off
= I2S_CH0_OFFSET
+ ((chan_id
- 1) * 4);
49 writel(chan_id
, base
+ reg_off
);
56 static int xlnx_i2s_trigger(struct snd_pcm_substream
*substream
, int cmd
,
57 struct snd_soc_dai
*i2s_dai
)
59 void __iomem
*base
= snd_soc_dai_get_drvdata(i2s_dai
);
62 case SNDRV_PCM_TRIGGER_START
:
63 case SNDRV_PCM_TRIGGER_RESUME
:
64 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
65 writel(1, base
+ I2S_CORE_CTRL_OFFSET
);
67 case SNDRV_PCM_TRIGGER_STOP
:
68 case SNDRV_PCM_TRIGGER_SUSPEND
:
69 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
70 writel(0, base
+ I2S_CORE_CTRL_OFFSET
);
79 static const struct snd_soc_dai_ops xlnx_i2s_dai_ops
= {
80 .trigger
= xlnx_i2s_trigger
,
81 .set_clkdiv
= xlnx_i2s_set_sclkout_div
,
82 .hw_params
= xlnx_i2s_hw_params
85 static const struct snd_soc_component_driver xlnx_i2s_component
= {
89 static const struct of_device_id xlnx_i2s_of_match
[] = {
90 { .compatible
= "xlnx,i2s-transmitter-1.0", },
91 { .compatible
= "xlnx,i2s-receiver-1.0", },
94 MODULE_DEVICE_TABLE(of
, xlnx_i2s_of_match
);
96 static int xlnx_i2s_probe(struct platform_device
*pdev
)
99 struct snd_soc_dai_driver
*dai_drv
;
101 u32 ch
, format
, data_width
;
102 struct device
*dev
= &pdev
->dev
;
103 struct device_node
*node
= dev
->of_node
;
105 dai_drv
= devm_kzalloc(&pdev
->dev
, sizeof(*dai_drv
), GFP_KERNEL
);
109 base
= devm_platform_ioremap_resource(pdev
, 0);
111 return PTR_ERR(base
);
113 ret
= of_property_read_u32(node
, "xlnx,num-channels", &ch
);
115 dev_err(dev
, "cannot get supported channels\n");
120 ret
= of_property_read_u32(node
, "xlnx,dwidth", &data_width
);
122 dev_err(dev
, "cannot get data width\n");
125 switch (data_width
) {
127 format
= SNDRV_PCM_FMTBIT_S16_LE
;
130 format
= SNDRV_PCM_FMTBIT_S24_LE
;
136 if (of_device_is_compatible(node
, "xlnx,i2s-transmitter-1.0")) {
137 dai_drv
->name
= "xlnx_i2s_playback";
138 dai_drv
->playback
.stream_name
= "Playback";
139 dai_drv
->playback
.formats
= format
;
140 dai_drv
->playback
.channels_min
= ch
;
141 dai_drv
->playback
.channels_max
= ch
;
142 dai_drv
->playback
.rates
= SNDRV_PCM_RATE_8000_192000
;
143 dai_drv
->ops
= &xlnx_i2s_dai_ops
;
144 } else if (of_device_is_compatible(node
, "xlnx,i2s-receiver-1.0")) {
145 dai_drv
->name
= "xlnx_i2s_capture";
146 dai_drv
->capture
.stream_name
= "Capture";
147 dai_drv
->capture
.formats
= format
;
148 dai_drv
->capture
.channels_min
= ch
;
149 dai_drv
->capture
.channels_max
= ch
;
150 dai_drv
->capture
.rates
= SNDRV_PCM_RATE_8000_192000
;
151 dai_drv
->ops
= &xlnx_i2s_dai_ops
;
156 dev_set_drvdata(&pdev
->dev
, base
);
158 ret
= devm_snd_soc_register_component(&pdev
->dev
, &xlnx_i2s_component
,
161 dev_err(&pdev
->dev
, "i2s component registration failed\n");
165 dev_info(&pdev
->dev
, "%s DAI registered\n", dai_drv
->name
);
170 static struct platform_driver xlnx_i2s_aud_driver
= {
173 .of_match_table
= xlnx_i2s_of_match
,
175 .probe
= xlnx_i2s_probe
,
178 module_platform_driver(xlnx_i2s_aud_driver
);
180 MODULE_LICENSE("GPL v2");
181 MODULE_AUTHOR("Praveen Vuppala <praveenv@xilinx.com>");
182 MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>");