Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
51706 views
1
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2
/*
3
* dw-hdmi-gp-audio.c
4
*
5
* Copyright 2020-2022 NXP
6
*/
7
#include <linux/io.h>
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>
16
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>
25
26
#include "dw-hdmi-audio.h"
27
28
#define DRIVER_NAME "dw-hdmi-gp-audio"
29
#define DRV_NAME "hdmi-gp-audio"
30
31
struct snd_dw_hdmi {
32
struct dw_hdmi_audio_data data;
33
struct platform_device *audio_pdev;
34
unsigned int pos;
35
};
36
37
struct dw_hdmi_channel_conf {
38
u8 conf1;
39
u8 ca;
40
};
41
42
/*
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.
46
*
47
* Mappings for alsa-lib pcm/surround*.conf files:
48
*
49
* Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
50
* Channels 2 4 6 6 6 8
51
*
52
* Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
53
*
54
* Number of ALSA channels
55
* ALSA Channel 2 3 4 5 6 7 8
56
* 0 FL:0 = = = = = =
57
* 1 FR:1 = = = = = =
58
* 2 FC:3 RL:4 LFE:2 = = =
59
* 3 RR:5 RL:4 FC:3 = =
60
* 4 RR:5 RL:4 = =
61
* 5 RR:5 = =
62
* 6 RC:6 =
63
* 7 RLC/FRC RLC/FRC
64
*/
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 */
73
};
74
75
static int audio_hw_params(struct device *dev, void *data,
76
struct hdmi_codec_daifmt *daifmt,
77
struct hdmi_codec_params *params)
78
{
79
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
80
u8 ca;
81
82
dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
83
84
ca = default_hdmi_channel_config[params->channels - 2].ca;
85
86
dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
87
dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
88
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);
92
93
if (daifmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
94
dw_hdmi_set_sample_iec958(dw->data.hdmi, 1);
95
else
96
dw_hdmi_set_sample_iec958(dw->data.hdmi, 0);
97
98
return 0;
99
}
100
101
static void audio_shutdown(struct device *dev, void *data)
102
{
103
}
104
105
static int audio_mute_stream(struct device *dev, void *data,
106
bool enable, int direction)
107
{
108
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
109
110
if (!enable)
111
dw_hdmi_audio_enable(dw->data.hdmi);
112
else
113
dw_hdmi_audio_disable(dw->data.hdmi);
114
115
return 0;
116
}
117
118
static int audio_get_eld(struct device *dev, void *data,
119
u8 *buf, size_t len)
120
{
121
struct dw_hdmi_audio_data *audio = data;
122
u8 *eld;
123
124
eld = audio->get_eld(audio->hdmi);
125
if (eld)
126
memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
127
else
128
/* Pass en empty ELD if connector not available */
129
memset(buf, 0, len);
130
131
return 0;
132
}
133
134
static int audio_hook_plugged_cb(struct device *dev, void *data,
135
hdmi_codec_plugged_cb fn,
136
struct device *codec_dev)
137
{
138
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
139
140
return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
141
}
142
143
static const struct hdmi_codec_ops audio_codec_ops = {
144
.hw_params = audio_hw_params,
145
.audio_shutdown = audio_shutdown,
146
.mute_stream = audio_mute_stream,
147
.get_eld = audio_get_eld,
148
.hook_plugged_cb = audio_hook_plugged_cb,
149
};
150
151
static int snd_dw_hdmi_probe(struct platform_device *pdev)
152
{
153
struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
154
struct snd_dw_hdmi *dw;
155
156
const struct hdmi_codec_pdata codec_data = {
157
.i2s = 1,
158
.spdif = 0,
159
.ops = &audio_codec_ops,
160
.max_i2s_channels = 8,
161
.data = data,
162
};
163
164
dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
165
if (!dw)
166
return -ENOMEM;
167
168
dw->data = *data;
169
170
platform_set_drvdata(pdev, dw);
171
172
dw->audio_pdev = platform_device_register_data(&pdev->dev,
173
HDMI_CODEC_DRV_NAME, 1,
174
&codec_data,
175
sizeof(codec_data));
176
177
return PTR_ERR_OR_ZERO(dw->audio_pdev);
178
}
179
180
static void snd_dw_hdmi_remove(struct platform_device *pdev)
181
{
182
struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
183
184
platform_device_unregister(dw->audio_pdev);
185
}
186
187
static struct platform_driver snd_dw_hdmi_driver = {
188
.probe = snd_dw_hdmi_probe,
189
.remove = snd_dw_hdmi_remove,
190
.driver = {
191
.name = DRIVER_NAME,
192
},
193
};
194
195
module_platform_driver(snd_dw_hdmi_driver);
196
197
MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
198
MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
199
MODULE_LICENSE("GPL");
200
MODULE_ALIAS("platform:" DRIVER_NAME);
201
202