1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
5 * Copyright 2020-2022 NXP
8 #include <linux/interrupt.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/dmaengine.h>
12 #include <linux/dma-mapping.h>
13 #include <drm/bridge/dw_hdmi.h>
14 #include <drm/drm_edid.h>
15 #include <drm/drm_connector.h>
17 #include <sound/hdmi-codec.h>
18 #include <sound/asoundef.h>
19 #include <sound/core.h>
20 #include <sound/initval.h>
21 #include <sound/pcm.h>
22 #include <sound/pcm_drm_eld.h>
23 #include <sound/pcm_iec958.h>
24 #include <sound/dmaengine_pcm.h>
26 #include "dw-hdmi-audio.h"
28 #define DRIVER_NAME "dw-hdmi-gp-audio"
29 #define DRV_NAME "hdmi-gp-audio"
32 struct dw_hdmi_audio_data data
;
33 struct platform_device
*audio_pdev
;
37 struct dw_hdmi_channel_conf
{
43 * The default mapping of ALSA channels to HDMI channels and speaker
44 * allocation bits. Note that we can't do channel remapping here -
45 * channels must be in the same order.
47 * Mappings for alsa-lib pcm/surround*.conf files:
49 * Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
50 * Channels 2 4 6 6 6 8
52 * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
54 * Number of ALSA channels
55 * ALSA Channel 2 3 4 5 6 7 8
58 * 2 FC:3 RL:4 LFE:2 = = =
59 * 3 RR:5 RL:4 FC:3 = =
65 static struct dw_hdmi_channel_conf default_hdmi_channel_config
[7] = {
66 { 0x03, 0x00 }, /* FL,FR */
67 { 0x0b, 0x02 }, /* FL,FR,FC */
68 { 0x33, 0x08 }, /* FL,FR,RL,RR */
69 { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
70 { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
71 { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
72 { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
75 static int audio_hw_params(struct device
*dev
, void *data
,
76 struct hdmi_codec_daifmt
*daifmt
,
77 struct hdmi_codec_params
*params
)
79 struct snd_dw_hdmi
*dw
= dev_get_drvdata(dev
);
82 dw_hdmi_set_sample_rate(dw
->data
.hdmi
, params
->sample_rate
);
84 ca
= default_hdmi_channel_config
[params
->channels
- 2].ca
;
86 dw_hdmi_set_channel_count(dw
->data
.hdmi
, params
->channels
);
87 dw_hdmi_set_channel_allocation(dw
->data
.hdmi
, ca
);
89 dw_hdmi_set_sample_non_pcm(dw
->data
.hdmi
,
90 params
->iec
.status
[0] & IEC958_AES0_NONAUDIO
);
91 dw_hdmi_set_sample_width(dw
->data
.hdmi
, params
->sample_width
);
96 static void audio_shutdown(struct device
*dev
, void *data
)
100 static int audio_mute_stream(struct device
*dev
, void *data
,
101 bool enable
, int direction
)
103 struct snd_dw_hdmi
*dw
= dev_get_drvdata(dev
);
106 dw_hdmi_audio_enable(dw
->data
.hdmi
);
108 dw_hdmi_audio_disable(dw
->data
.hdmi
);
113 static int audio_get_eld(struct device
*dev
, void *data
,
116 struct dw_hdmi_audio_data
*audio
= data
;
119 eld
= audio
->get_eld(audio
->hdmi
);
121 memcpy(buf
, eld
, min_t(size_t, MAX_ELD_BYTES
, len
));
123 /* Pass en empty ELD if connector not available */
129 static int audio_hook_plugged_cb(struct device
*dev
, void *data
,
130 hdmi_codec_plugged_cb fn
,
131 struct device
*codec_dev
)
133 struct snd_dw_hdmi
*dw
= dev_get_drvdata(dev
);
135 return dw_hdmi_set_plugged_cb(dw
->data
.hdmi
, fn
, codec_dev
);
138 static const struct hdmi_codec_ops audio_codec_ops
= {
139 .hw_params
= audio_hw_params
,
140 .audio_shutdown
= audio_shutdown
,
141 .mute_stream
= audio_mute_stream
,
142 .get_eld
= audio_get_eld
,
143 .hook_plugged_cb
= audio_hook_plugged_cb
,
146 static int snd_dw_hdmi_probe(struct platform_device
*pdev
)
148 struct dw_hdmi_audio_data
*data
= pdev
->dev
.platform_data
;
149 struct snd_dw_hdmi
*dw
;
151 const struct hdmi_codec_pdata codec_data
= {
154 .ops
= &audio_codec_ops
,
155 .max_i2s_channels
= 8,
159 dw
= devm_kzalloc(&pdev
->dev
, sizeof(*dw
), GFP_KERNEL
);
165 platform_set_drvdata(pdev
, dw
);
167 dw
->audio_pdev
= platform_device_register_data(&pdev
->dev
,
168 HDMI_CODEC_DRV_NAME
, 1,
172 return PTR_ERR_OR_ZERO(dw
->audio_pdev
);
175 static void snd_dw_hdmi_remove(struct platform_device
*pdev
)
177 struct snd_dw_hdmi
*dw
= platform_get_drvdata(pdev
);
179 platform_device_unregister(dw
->audio_pdev
);
182 static struct platform_driver snd_dw_hdmi_driver
= {
183 .probe
= snd_dw_hdmi_probe
,
184 .remove
= snd_dw_hdmi_remove
,
190 module_platform_driver(snd_dw_hdmi_driver
);
192 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
193 MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
194 MODULE_LICENSE("GPL");
195 MODULE_ALIAS("platform:" DRIVER_NAME
);