hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
....@@ -1,14 +1,16 @@
1
+// SPDX-License-Identifier: GPL-2.0
12 /*
23 * dw-hdmi-i2s-audio.c
34 *
45 * Copyright (c) 2017 Renesas Solutions Corp.
56 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6
- *
7
- * This program is free software; you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License version 2 as
9
- * published by the Free Software Foundation.
107 */
8
+
9
+#include <linux/dma-mapping.h>
10
+#include <linux/module.h>
11
+
1112 #include <drm/bridge/dw_hdmi.h>
13
+#include <drm/drm_crtc.h>
1214
1315 #include <sound/hdmi-codec.h>
1416
....@@ -32,14 +34,6 @@
3234 return audio->read(hdmi, offset);
3335 }
3436
35
-static inline void hdmi_update_bits(struct dw_hdmi_i2s_audio_data *audio,
36
- u8 data, u8 mask, unsigned int reg)
37
-{
38
- struct dw_hdmi *hdmi = audio->hdmi;
39
-
40
- audio->mod(hdmi, data, mask, reg);
41
-}
42
-
4337 static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
4438 struct hdmi_codec_daifmt *fmt,
4539 struct hdmi_codec_params *hparms)
....@@ -48,19 +42,34 @@
4842 struct dw_hdmi *hdmi = audio->hdmi;
4943 u8 conf0 = 0;
5044 u8 conf1 = 0;
45
+ u8 conf2 = 0;
5146 u8 inputclkfs = 0;
52
- u8 val;
53
-
54
- dw_hdmi_audio_disable(hdmi);
5547
5648 /* it cares I2S only */
57
- if ((fmt->fmt != HDMI_I2S) ||
58
- (fmt->bit_clk_master | fmt->frame_clk_master)) {
59
- dev_err(dev, "unsupported format/settings\n");
49
+ if (fmt->bit_clk_master | fmt->frame_clk_master) {
50
+ dev_err(dev, "unsupported clock settings\n");
6051 return -EINVAL;
6152 }
6253
63
- inputclkfs = HDMI_AUD_INPUTCLKFS_128FS;
54
+ /* Reset the FIFOs before applying new params */
55
+ hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
56
+ hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2SSWRST_REQ, HDMI_MC_SWRSTZ);
57
+
58
+ inputclkfs = HDMI_AUD_INPUTCLKFS_64FS;
59
+ conf0 = (HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_EN0);
60
+
61
+ /* Enable the required i2s lanes */
62
+ switch (hparms->channels) {
63
+ case 7 ... 8:
64
+ conf0 |= HDMI_AUD_CONF0_I2S_EN3;
65
+ fallthrough;
66
+ case 5 ... 6:
67
+ conf0 |= HDMI_AUD_CONF0_I2S_EN2;
68
+ fallthrough;
69
+ case 3 ... 4:
70
+ conf0 |= HDMI_AUD_CONF0_I2S_EN1;
71
+ /* Fall-thru */
72
+ }
6473
6574 switch (hparms->sample_width) {
6675 case 16:
....@@ -70,136 +79,89 @@
7079 case 32:
7180 conf1 = HDMI_AUD_CONF1_WIDTH_24;
7281 break;
82
+ }
83
+
84
+ switch (fmt->fmt) {
85
+ case HDMI_I2S:
86
+ conf1 |= HDMI_AUD_CONF1_MODE_I2S;
87
+ break;
88
+ case HDMI_RIGHT_J:
89
+ conf1 |= HDMI_AUD_CONF1_MODE_RIGHT_J;
90
+ break;
91
+ case HDMI_LEFT_J:
92
+ conf1 |= HDMI_AUD_CONF1_MODE_LEFT_J;
93
+ break;
94
+ case HDMI_DSP_A:
95
+ conf1 |= HDMI_AUD_CONF1_MODE_BURST_1;
96
+ break;
97
+ case HDMI_DSP_B:
98
+ conf1 |= HDMI_AUD_CONF1_MODE_BURST_2;
99
+ break;
73100 default:
74
- dev_err(dev, "unsupported sample width [%d]\n", hparms->sample_width);
101
+ dev_err(dev, "unsupported format\n");
75102 return -EINVAL;
76103 }
77104
78
- switch (hparms->channels) {
79
- case 2:
80
- conf0 = HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE;
81
- break;
82
- case 4:
83
- conf0 = HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE;
84
- break;
85
- case 6:
86
- conf0 = HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE;
87
- break;
88
- case 8:
89
- conf0 = HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE;
105
+ switch (fmt->bit_fmt) {
106
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
107
+ conf1 = HDMI_AUD_CONF1_WIDTH_21;
108
+ conf2 = (hparms->channels == 8) ? HDMI_AUD_CONF2_HBR : HDMI_AUD_CONF2_NLPCM;
90109 break;
91110 default:
92
- dev_err(dev, "unsupported channels [%d]\n", hparms->channels);
93
- return -EINVAL;
94
- }
95
-
96
- /*
97
- * dw-hdmi introduced insert_pcuv bit in version 2.10a.
98
- * When set (1'b1), this bit enables the insertion of the PCUV
99
- * (Parity, Channel Status, User bit and Validity) bits on the
100
- * incoming audio stream (support limited to Linear PCM audio)
101
- */
102
- val = 0;
103
- if (hdmi_read(audio, HDMI_DESIGN_ID) >= 0x21)
104
- val = HDMI_AUD_CONF2_INSERT_PCUV;
105
-
106
- /*Mask fifo empty and full int and reset fifo*/
107
- hdmi_update_bits(audio,
108
- HDMI_AUD_INT_FIFO_EMPTY_MSK |
109
- HDMI_AUD_INT_FIFO_FULL_MSK,
110
- HDMI_AUD_INT_FIFO_EMPTY_MSK |
111
- HDMI_AUD_INT_FIFO_FULL_MSK, HDMI_AUD_INT);
112
- hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET,
113
- HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
114
- hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK,
115
- HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ);
116
-
117
- switch (hparms->mode) {
118
- case NLPCM:
119
- hdmi_write(audio, HDMI_AUD_CONF2_NLPCM, HDMI_AUD_CONF2);
120
- conf1 = HDMI_AUD_CONF1_WIDTH_21;
121
- break;
122
- case HBR:
123
- hdmi_write(audio, HDMI_AUD_CONF2_HBR, HDMI_AUD_CONF2);
124
- conf1 = HDMI_AUD_CONF1_WIDTH_21;
125
- break;
126
- default:
127
- hdmi_write(audio, val, HDMI_AUD_CONF2);
111
+ /*
112
+ * dw-hdmi introduced insert_pcuv bit in version 2.10a.
113
+ * When set (1'b1), this bit enables the insertion of the PCUV
114
+ * (Parity, Channel Status, User bit and Validity) bits on the
115
+ * incoming audio stream (support limited to Linear PCM audio)
116
+ */
117
+ if (hdmi_read(audio, HDMI_DESIGN_ID) >= 0x21)
118
+ conf2 = HDMI_AUD_CONF2_INSERT_PCUV;
128119 break;
129120 }
130121
131122 dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
123
+ dw_hdmi_set_channel_status(hdmi, hparms->iec.status);
124
+ dw_hdmi_set_channel_count(hdmi, hparms->channels);
125
+ dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation);
132126
133127 hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
134128 hdmi_write(audio, conf0, HDMI_AUD_CONF0);
135129 hdmi_write(audio, conf1, HDMI_AUD_CONF1);
130
+ hdmi_write(audio, conf2, HDMI_AUD_CONF2);
136131
137
- val = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0;
138
- if (hparms->channels > 2)
139
- val = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1;
140
- hdmi_update_bits(audio, val, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK,
141
- HDMI_FC_AUDSCONF);
132
+ return 0;
133
+}
142134
143
- switch (hparms->sample_rate) {
144
- case 32000:
145
- val = HDMI_FC_AUDSCHNLS_32K;
146
- break;
147
- case 44100:
148
- val = HDMI_FC_AUDSCHNLS_441K;
149
- break;
150
- case 48000:
151
- val = HDMI_FC_AUDSCHNLS_48K;
152
- break;
153
- case 88200:
154
- val = HDMI_FC_AUDSCHNLS_882K;
155
- break;
156
- case 96000:
157
- val = HDMI_FC_AUDSCHNLS_96K;
158
- break;
159
- case 176400:
160
- val = HDMI_FC_AUDSCHNLS_1764K;
161
- break;
162
- case 192000:
163
- val = HDMI_FC_AUDSCHNLS_192K;
164
- break;
165
- default:
166
- val = HDMI_FC_AUDSCHNLS_441K;
167
- break;
168
- }
169
-
170
- /* set channel status register */
171
- hdmi_update_bits(audio, val,
172
- HDMI_FC_AUDSCHNLS7_SAMPFREQ_MASK,
173
- HDMI_FC_AUDSCHNLS7);
174
- hdmi_write(audio,
175
- (((u8)~val) << HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET),
176
- HDMI_FC_AUDSCHNLS8);
177
-
178
- /* Refer to CEA861-E Audio infoFrame
179
- * Set both Audio Channel Count and Audio Coding
180
- * Type Refer to Stream Head for HDMI
181
- */
182
- hdmi_update_bits(audio,
183
- (hparms->channels - 1) << HDMI_FC_AUDICONF0_CC_OFFSET,
184
- HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0);
185
-
186
- /* Set both Audio Sample Size and Sample Frequency
187
- * Refer to Stream Head for HDMI
188
- */
189
- hdmi_write(audio, 0x00, HDMI_FC_AUDICONF1);
190
-
191
- /* Set Channel Allocation */
192
- hdmi_write(audio, 0x00, HDMI_FC_AUDICONF2);
193
-
194
- /* Set LFEPBLDOWN-MIX INH and LSV */
195
- hdmi_write(audio, 0x00, HDMI_FC_AUDICONF3);
135
+static int dw_hdmi_i2s_audio_startup(struct device *dev, void *data)
136
+{
137
+ struct dw_hdmi_i2s_audio_data *audio = data;
138
+ struct dw_hdmi *hdmi = audio->hdmi;
196139
197140 dw_hdmi_audio_enable(hdmi);
198141
199
- hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET,
200
- HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
201
- hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK,
202
- HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ);
142
+ return 0;
143
+}
144
+
145
+static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
146
+{
147
+ struct dw_hdmi_i2s_audio_data *audio = data;
148
+ struct dw_hdmi *hdmi = audio->hdmi;
149
+
150
+ dw_hdmi_audio_disable(hdmi);
151
+}
152
+
153
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
154
+ size_t len)
155
+{
156
+ struct dw_hdmi_i2s_audio_data *audio = data;
157
+ u8 *eld;
158
+
159
+ eld = audio->get_eld(audio->hdmi);
160
+ if (eld)
161
+ memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
162
+ else
163
+ /* Pass en empty ELD if connector not available */
164
+ memset(buf, 0, len);
203165
204166 return 0;
205167 }
....@@ -236,6 +198,9 @@
236198
237199 static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
238200 .hw_params = dw_hdmi_i2s_hw_params,
201
+ .audio_startup = dw_hdmi_i2s_audio_startup,
202
+ .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
203
+ .get_eld = dw_hdmi_i2s_get_eld,
239204 .get_dai_id = dw_hdmi_i2s_get_dai_id,
240205 .hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb,
241206 };