Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/display/drm_hdmi_audio_helper.c
26494 views
1
// SPDX-License-Identifier: MIT
2
/*
3
* Copyright (c) 2024 Linaro Ltd
4
*/
5
6
#include <linux/export.h>
7
#include <linux/mutex.h>
8
#include <linux/of_graph.h>
9
#include <linux/platform_device.h>
10
11
#include <drm/drm_connector.h>
12
#include <drm/drm_device.h>
13
#include <drm/display/drm_hdmi_audio_helper.h>
14
15
#include <sound/hdmi-codec.h>
16
17
static int drm_connector_hdmi_audio_startup(struct device *dev, void *data)
18
{
19
struct drm_connector *connector = data;
20
const struct drm_connector_hdmi_audio_funcs *funcs =
21
connector->hdmi_audio.funcs;
22
23
if (funcs->startup)
24
return funcs->startup(connector);
25
26
return 0;
27
}
28
29
static int drm_connector_hdmi_audio_prepare(struct device *dev, void *data,
30
struct hdmi_codec_daifmt *fmt,
31
struct hdmi_codec_params *hparms)
32
{
33
struct drm_connector *connector = data;
34
const struct drm_connector_hdmi_audio_funcs *funcs =
35
connector->hdmi_audio.funcs;
36
37
return funcs->prepare(connector, fmt, hparms);
38
}
39
40
static void drm_connector_hdmi_audio_shutdown(struct device *dev, void *data)
41
{
42
struct drm_connector *connector = data;
43
const struct drm_connector_hdmi_audio_funcs *funcs =
44
connector->hdmi_audio.funcs;
45
46
return funcs->shutdown(connector);
47
}
48
49
static int drm_connector_hdmi_audio_mute_stream(struct device *dev, void *data,
50
bool enable, int direction)
51
{
52
struct drm_connector *connector = data;
53
const struct drm_connector_hdmi_audio_funcs *funcs =
54
connector->hdmi_audio.funcs;
55
56
if (funcs->mute_stream)
57
return funcs->mute_stream(connector, enable, direction);
58
59
return -ENOTSUPP;
60
}
61
62
static int drm_connector_hdmi_audio_get_dai_id(struct snd_soc_component *comment,
63
struct device_node *endpoint,
64
void *data)
65
{
66
struct drm_connector *connector = data;
67
struct of_endpoint of_ep;
68
int ret;
69
70
if (connector->hdmi_audio.dai_port < 0)
71
return -ENOTSUPP;
72
73
ret = of_graph_parse_endpoint(endpoint, &of_ep);
74
if (ret < 0)
75
return ret;
76
77
if (of_ep.port == connector->hdmi_audio.dai_port)
78
return 0;
79
80
return -EINVAL;
81
}
82
83
static int drm_connector_hdmi_audio_get_eld(struct device *dev, void *data,
84
uint8_t *buf, size_t len)
85
{
86
struct drm_connector *connector = data;
87
88
mutex_lock(&connector->eld_mutex);
89
memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
90
mutex_unlock(&connector->eld_mutex);
91
92
return 0;
93
}
94
95
static int drm_connector_hdmi_audio_hook_plugged_cb(struct device *dev,
96
void *data,
97
hdmi_codec_plugged_cb fn,
98
struct device *codec_dev)
99
{
100
struct drm_connector *connector = data;
101
102
mutex_lock(&connector->hdmi_audio.lock);
103
104
connector->hdmi_audio.plugged_cb = fn;
105
connector->hdmi_audio.plugged_cb_dev = codec_dev;
106
107
if (fn)
108
fn(codec_dev, connector->hdmi_audio.last_state);
109
110
mutex_unlock(&connector->hdmi_audio.lock);
111
112
return 0;
113
}
114
115
void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector,
116
bool plugged)
117
{
118
mutex_lock(&connector->hdmi_audio.lock);
119
120
connector->hdmi_audio.last_state = plugged;
121
122
if (connector->hdmi_audio.plugged_cb &&
123
connector->hdmi_audio.plugged_cb_dev)
124
connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev,
125
connector->hdmi_audio.last_state);
126
127
mutex_unlock(&connector->hdmi_audio.lock);
128
}
129
EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify);
130
131
static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = {
132
.audio_startup = drm_connector_hdmi_audio_startup,
133
.prepare = drm_connector_hdmi_audio_prepare,
134
.audio_shutdown = drm_connector_hdmi_audio_shutdown,
135
.mute_stream = drm_connector_hdmi_audio_mute_stream,
136
.get_eld = drm_connector_hdmi_audio_get_eld,
137
.get_dai_id = drm_connector_hdmi_audio_get_dai_id,
138
.hook_plugged_cb = drm_connector_hdmi_audio_hook_plugged_cb,
139
};
140
141
/**
142
* drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector
143
* @connector: A pointer to the connector to allocate codec for
144
* @hdmi_codec_dev: device to be used as a parent for the HDMI Codec
145
* @funcs: callbacks for this HDMI Codec
146
* @max_i2s_playback_channels: maximum number of playback I2S channels
147
* @i2s_formats: set of I2S formats (use 0 for a bus-specific set)
148
* @spdif_playback: set if HDMI codec has S/PDIF playback port
149
* @dai_port: sound DAI port, -1 if it is not enabled
150
*
151
* Create a HDMI codec device to be used with the specified connector.
152
*
153
* Returns:
154
* Zero on success, error code on failure.
155
*/
156
int drm_connector_hdmi_audio_init(struct drm_connector *connector,
157
struct device *hdmi_codec_dev,
158
const struct drm_connector_hdmi_audio_funcs *funcs,
159
unsigned int max_i2s_playback_channels,
160
u64 i2s_formats,
161
bool spdif_playback,
162
int dai_port)
163
{
164
struct hdmi_codec_pdata codec_pdata = {
165
.ops = &drm_connector_hdmi_audio_ops,
166
.max_i2s_channels = max_i2s_playback_channels,
167
.i2s = !!max_i2s_playback_channels,
168
.i2s_formats = i2s_formats,
169
.spdif = spdif_playback,
170
.no_i2s_capture = true,
171
.no_spdif_capture = true,
172
.data = connector,
173
};
174
struct platform_device *pdev;
175
176
if (!funcs ||
177
!funcs->prepare ||
178
!funcs->shutdown)
179
return -EINVAL;
180
181
connector->hdmi_audio.funcs = funcs;
182
connector->hdmi_audio.dai_port = dai_port;
183
184
pdev = platform_device_register_data(hdmi_codec_dev,
185
HDMI_CODEC_DRV_NAME,
186
PLATFORM_DEVID_AUTO,
187
&codec_pdata, sizeof(codec_pdata));
188
if (IS_ERR(pdev))
189
return PTR_ERR(pdev);
190
191
connector->hdmi_audio.codec_pdev = pdev;
192
193
return 0;
194
}
195
EXPORT_SYMBOL(drm_connector_hdmi_audio_init);
196
197