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_master
| fmt
->frame_clk_master
) {
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
;
139 memcpy(buf
, audio
->eld
, min_t(size_t, MAX_ELD_BYTES
, len
));
143 static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component
*component
,
144 struct device_node
*endpoint
)
146 struct of_endpoint of_ep
;
149 ret
= of_graph_parse_endpoint(endpoint
, &of_ep
);
154 * HDMI sound should be located as reg = <2>
155 * Then, it is sound port 0
163 static int dw_hdmi_i2s_hook_plugged_cb(struct device
*dev
, void *data
,
164 hdmi_codec_plugged_cb fn
,
165 struct device
*codec_dev
)
167 struct dw_hdmi_i2s_audio_data
*audio
= data
;
168 struct dw_hdmi
*hdmi
= audio
->hdmi
;
170 return dw_hdmi_set_plugged_cb(hdmi
, fn
, codec_dev
);
173 static const struct hdmi_codec_ops dw_hdmi_i2s_ops
= {
174 .hw_params
= dw_hdmi_i2s_hw_params
,
175 .audio_startup
= dw_hdmi_i2s_audio_startup
,
176 .audio_shutdown
= dw_hdmi_i2s_audio_shutdown
,
177 .get_eld
= dw_hdmi_i2s_get_eld
,
178 .get_dai_id
= dw_hdmi_i2s_get_dai_id
,
179 .hook_plugged_cb
= dw_hdmi_i2s_hook_plugged_cb
,
182 static int snd_dw_hdmi_probe(struct platform_device
*pdev
)
184 struct dw_hdmi_i2s_audio_data
*audio
= pdev
->dev
.platform_data
;
185 struct platform_device_info pdevinfo
;
186 struct hdmi_codec_pdata pdata
;
187 struct platform_device
*platform
;
189 pdata
.ops
= &dw_hdmi_i2s_ops
;
191 pdata
.max_i2s_channels
= 8;
194 memset(&pdevinfo
, 0, sizeof(pdevinfo
));
195 pdevinfo
.parent
= pdev
->dev
.parent
;
196 pdevinfo
.id
= PLATFORM_DEVID_AUTO
;
197 pdevinfo
.name
= HDMI_CODEC_DRV_NAME
;
198 pdevinfo
.data
= &pdata
;
199 pdevinfo
.size_data
= sizeof(pdata
);
200 pdevinfo
.dma_mask
= DMA_BIT_MASK(32);
202 platform
= platform_device_register_full(&pdevinfo
);
203 if (IS_ERR(platform
))
204 return PTR_ERR(platform
);
206 dev_set_drvdata(&pdev
->dev
, platform
);
211 static int snd_dw_hdmi_remove(struct platform_device
*pdev
)
213 struct platform_device
*platform
= dev_get_drvdata(&pdev
->dev
);
215 platform_device_unregister(platform
);
220 static struct platform_driver snd_dw_hdmi_driver
= {
221 .probe
= snd_dw_hdmi_probe
,
222 .remove
= snd_dw_hdmi_remove
,
227 module_platform_driver(snd_dw_hdmi_driver
);
229 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
230 MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
231 MODULE_LICENSE("GPL v2");
232 MODULE_ALIAS("platform:" DRIVER_NAME
);