1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (c) 2017 Renesas Solutions Corp.
6 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
9 #include <linux/dma-mapping.h>
10 #include <linux/module.h>
12 #include <drm/bridge/dw_hdmi.h>
13 #include <drm/drm_crtc.h>
15 #include <sound/hdmi-codec.h>
18 #include "dw-hdmi-audio.h"
20 #define DRIVER_NAME "dw-hdmi-i2s-audio"
22 static inline void hdmi_write(struct dw_hdmi_i2s_audio_data
*audio
,
25 struct dw_hdmi
*hdmi
= audio
->hdmi
;
27 audio
->write(hdmi
, val
, offset
);
30 static inline u8
hdmi_read(struct dw_hdmi_i2s_audio_data
*audio
, int offset
)
32 struct dw_hdmi
*hdmi
= audio
->hdmi
;
34 return audio
->read(hdmi
, offset
);
37 static int dw_hdmi_i2s_hw_params(struct device
*dev
, void *data
,
38 struct hdmi_codec_daifmt
*fmt
,
39 struct hdmi_codec_params
*hparms
)
41 struct dw_hdmi_i2s_audio_data
*audio
= data
;
42 struct dw_hdmi
*hdmi
= audio
->hdmi
;
47 /* it cares I2S only */
48 if (fmt
->bit_clk_provider
| fmt
->frame_clk_provider
) {
49 dev_err(dev
, "unsupported clock settings\n");
53 /* Reset the FIFOs before applying new params */
54 hdmi_write(audio
, HDMI_AUD_CONF0_SW_RESET
, HDMI_AUD_CONF0
);
55 hdmi_write(audio
, (u8
)~HDMI_MC_SWRSTZ_I2SSWRST_REQ
, HDMI_MC_SWRSTZ
);
57 inputclkfs
= HDMI_AUD_INPUTCLKFS_64FS
;
58 conf0
= (HDMI_AUD_CONF0_I2S_SELECT
| HDMI_AUD_CONF0_I2S_EN0
);
60 /* Enable the required i2s lanes */
61 switch (hparms
->channels
) {
63 conf0
|= HDMI_AUD_CONF0_I2S_EN3
;
66 conf0
|= HDMI_AUD_CONF0_I2S_EN2
;
69 conf0
|= HDMI_AUD_CONF0_I2S_EN1
;
73 switch (hparms
->sample_width
) {
75 conf1
= HDMI_AUD_CONF1_WIDTH_16
;
79 conf1
= HDMI_AUD_CONF1_WIDTH_24
;
85 conf1
|= HDMI_AUD_CONF1_MODE_I2S
;
88 conf1
|= HDMI_AUD_CONF1_MODE_RIGHT_J
;
91 conf1
|= HDMI_AUD_CONF1_MODE_LEFT_J
;
94 conf1
|= HDMI_AUD_CONF1_MODE_BURST_1
;
97 conf1
|= HDMI_AUD_CONF1_MODE_BURST_2
;
100 dev_err(dev
, "unsupported format\n");
104 dw_hdmi_set_sample_rate(hdmi
, hparms
->sample_rate
);
105 dw_hdmi_set_channel_status(hdmi
, hparms
->iec
.status
);
106 dw_hdmi_set_channel_count(hdmi
, hparms
->channels
);
107 dw_hdmi_set_channel_allocation(hdmi
, hparms
->cea
.channel_allocation
);
109 hdmi_write(audio
, inputclkfs
, HDMI_AUD_INPUTCLKFS
);
110 hdmi_write(audio
, conf0
, HDMI_AUD_CONF0
);
111 hdmi_write(audio
, conf1
, HDMI_AUD_CONF1
);
116 static int dw_hdmi_i2s_audio_startup(struct device
*dev
, void *data
)
118 struct dw_hdmi_i2s_audio_data
*audio
= data
;
119 struct dw_hdmi
*hdmi
= audio
->hdmi
;
121 dw_hdmi_audio_enable(hdmi
);
126 static void dw_hdmi_i2s_audio_shutdown(struct device
*dev
, void *data
)
128 struct dw_hdmi_i2s_audio_data
*audio
= data
;
129 struct dw_hdmi
*hdmi
= audio
->hdmi
;
131 dw_hdmi_audio_disable(hdmi
);
134 static int dw_hdmi_i2s_get_eld(struct device
*dev
, void *data
, uint8_t *buf
,
137 struct dw_hdmi_i2s_audio_data
*audio
= data
;
140 eld
= audio
->get_eld(audio
->hdmi
);
142 memcpy(buf
, eld
, min_t(size_t, MAX_ELD_BYTES
, len
));
144 /* Pass en empty ELD if connector not available */
150 static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component
*component
,
151 struct device_node
*endpoint
)
153 struct of_endpoint of_ep
;
156 ret
= of_graph_parse_endpoint(endpoint
, &of_ep
);
161 * HDMI sound should be located as reg = <2>
162 * Then, it is sound port 0
170 static int dw_hdmi_i2s_hook_plugged_cb(struct device
*dev
, void *data
,
171 hdmi_codec_plugged_cb fn
,
172 struct device
*codec_dev
)
174 struct dw_hdmi_i2s_audio_data
*audio
= data
;
175 struct dw_hdmi
*hdmi
= audio
->hdmi
;
177 return dw_hdmi_set_plugged_cb(hdmi
, fn
, codec_dev
);
180 static const struct hdmi_codec_ops dw_hdmi_i2s_ops
= {
181 .hw_params
= dw_hdmi_i2s_hw_params
,
182 .audio_startup
= dw_hdmi_i2s_audio_startup
,
183 .audio_shutdown
= dw_hdmi_i2s_audio_shutdown
,
184 .get_eld
= dw_hdmi_i2s_get_eld
,
185 .get_dai_id
= dw_hdmi_i2s_get_dai_id
,
186 .hook_plugged_cb
= dw_hdmi_i2s_hook_plugged_cb
,
189 static int snd_dw_hdmi_probe(struct platform_device
*pdev
)
191 struct dw_hdmi_i2s_audio_data
*audio
= pdev
->dev
.platform_data
;
192 struct platform_device_info pdevinfo
;
193 struct hdmi_codec_pdata pdata
;
194 struct platform_device
*platform
;
196 memset(&pdata
, 0, sizeof(pdata
));
197 pdata
.ops
= &dw_hdmi_i2s_ops
;
199 pdata
.max_i2s_channels
= 8;
202 memset(&pdevinfo
, 0, sizeof(pdevinfo
));
203 pdevinfo
.parent
= pdev
->dev
.parent
;
204 pdevinfo
.id
= PLATFORM_DEVID_AUTO
;
205 pdevinfo
.name
= HDMI_CODEC_DRV_NAME
;
206 pdevinfo
.data
= &pdata
;
207 pdevinfo
.size_data
= sizeof(pdata
);
208 pdevinfo
.dma_mask
= DMA_BIT_MASK(32);
210 platform
= platform_device_register_full(&pdevinfo
);
211 if (IS_ERR(platform
))
212 return PTR_ERR(platform
);
214 dev_set_drvdata(&pdev
->dev
, platform
);
219 static void snd_dw_hdmi_remove(struct platform_device
*pdev
)
221 struct platform_device
*platform
= dev_get_drvdata(&pdev
->dev
);
223 platform_device_unregister(platform
);
226 static struct platform_driver snd_dw_hdmi_driver
= {
227 .probe
= snd_dw_hdmi_probe
,
228 .remove
= snd_dw_hdmi_remove
,
233 module_platform_driver(snd_dw_hdmi_driver
);
235 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
236 MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
237 MODULE_LICENSE("GPL v2");
238 MODULE_ALIAS("platform:" DRIVER_NAME
);