Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/bridge: synopsys: Add audio support for dw-hdmi-qp

Register the dw-hdmi-qp bridge driver as an HDMI audio codec.

The register values computation functions (for n) are based on the
downstream driver, as well as the register writing functions.

The driver uses the generic HDMI Codec framework in order to implement
the HDMI audio support.

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Robert Foss <rfoss@kernel.org>
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20250217215641.372723-2-detlev.casanova@collabora.com

authored by

Sugar Zhang and committed by
Heiko Stuebner
fd0141d1 735ffae0

+489
+489
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
··· 36 36 37 37 #define SCRAMB_POLL_DELAY_MS 3000 38 38 39 + /* 40 + * Unless otherwise noted, entries in this table are 100% optimization. 41 + * Values can be obtained from dw_hdmi_qp_compute_n() but that function is 42 + * slow so we pre-compute values we expect to see. 43 + * 44 + * The values for TMDS 25175, 25200, 27000, 54000, 74250 and 148500 kHz are 45 + * the recommended N values specified in the Audio chapter of the HDMI 46 + * specification. 47 + */ 48 + static const struct dw_hdmi_audio_tmds_n { 49 + unsigned long tmds; 50 + unsigned int n_32k; 51 + unsigned int n_44k1; 52 + unsigned int n_48k; 53 + } common_tmds_n_table[] = { 54 + { .tmds = 25175000, .n_32k = 4576, .n_44k1 = 7007, .n_48k = 6864, }, 55 + { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 56 + { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 57 + { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, 58 + { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, 59 + { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 60 + { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 61 + { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 62 + { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 63 + { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 64 + { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 65 + { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 66 + { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 67 + { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 68 + { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, 69 + { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 70 + { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, 71 + { .tmds = 73250000, .n_32k = 11648, .n_44k1 = 14112, .n_48k = 6144, }, 72 + { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 73 + { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, 74 + { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 75 + { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 76 + { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 77 + { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 78 + { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 79 + { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 80 + { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 81 + { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 82 + { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 83 + { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 84 + { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, 85 + { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 86 + { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 87 + { .tmds = 146250000, .n_32k = 11648, .n_44k1 = 6272, .n_48k = 6144, }, 88 + { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 89 + { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 90 + { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 91 + 92 + /* For 297 MHz+ HDMI spec have some other rule for setting N */ 93 + { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, 94 + { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240,}, 95 + 96 + /* End of table */ 97 + { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, 98 + }; 99 + 100 + /* 101 + * These are the CTS values as recommended in the Audio chapter of the HDMI 102 + * specification. 103 + */ 104 + static const struct dw_hdmi_audio_tmds_cts { 105 + unsigned long tmds; 106 + unsigned int cts_32k; 107 + unsigned int cts_44k1; 108 + unsigned int cts_48k; 109 + } common_tmds_cts_table[] = { 110 + { .tmds = 25175000, .cts_32k = 28125, .cts_44k1 = 31250, .cts_48k = 28125, }, 111 + { .tmds = 25200000, .cts_32k = 25200, .cts_44k1 = 28000, .cts_48k = 25200, }, 112 + { .tmds = 27000000, .cts_32k = 27000, .cts_44k1 = 30000, .cts_48k = 27000, }, 113 + { .tmds = 54000000, .cts_32k = 54000, .cts_44k1 = 60000, .cts_48k = 54000, }, 114 + { .tmds = 74250000, .cts_32k = 74250, .cts_44k1 = 82500, .cts_48k = 74250, }, 115 + { .tmds = 148500000, .cts_32k = 148500, .cts_44k1 = 165000, .cts_48k = 148500, }, 116 + 117 + /* End of table */ 118 + { .tmds = 0, .cts_32k = 0, .cts_44k1 = 0, .cts_48k = 0, }, 119 + }; 120 + 39 121 struct dw_hdmi_qp_i2c { 40 122 struct i2c_adapter adap; 41 123 ··· 142 60 } phy; 143 61 144 62 struct regmap *regm; 63 + 64 + unsigned long tmds_char_rate; 145 65 }; 146 66 147 67 static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val, ··· 165 81 unsigned int mask, unsigned int reg) 166 82 { 167 83 regmap_update_bits(hdmi->regm, reg, mask, data); 84 + } 85 + 86 + static struct dw_hdmi_qp *dw_hdmi_qp_from_bridge(struct drm_bridge *bridge) 87 + { 88 + return container_of(bridge, struct dw_hdmi_qp, bridge); 89 + } 90 + 91 + static void dw_hdmi_qp_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts, 92 + unsigned int n) 93 + { 94 + /* Set N */ 95 + dw_hdmi_qp_mod(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0); 96 + 97 + /* Set CTS */ 98 + if (cts) 99 + dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK, 100 + AUDPKT_ACR_CONTROL1); 101 + else 102 + dw_hdmi_qp_mod(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK, 103 + AUDPKT_ACR_CONTROL1); 104 + 105 + dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK, 106 + AUDPKT_ACR_CONTROL1); 107 + } 108 + 109 + static int dw_hdmi_qp_match_tmds_n_table(struct dw_hdmi_qp *hdmi, 110 + unsigned long pixel_clk, 111 + unsigned long freq) 112 + { 113 + const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; 114 + int i; 115 + 116 + for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { 117 + if (pixel_clk == common_tmds_n_table[i].tmds) { 118 + tmds_n = &common_tmds_n_table[i]; 119 + break; 120 + } 121 + } 122 + 123 + if (!tmds_n) 124 + return -ENOENT; 125 + 126 + switch (freq) { 127 + case 32000: 128 + return tmds_n->n_32k; 129 + case 44100: 130 + case 88200: 131 + case 176400: 132 + return (freq / 44100) * tmds_n->n_44k1; 133 + case 48000: 134 + case 96000: 135 + case 192000: 136 + return (freq / 48000) * tmds_n->n_48k; 137 + default: 138 + return -ENOENT; 139 + } 140 + } 141 + 142 + static u32 dw_hdmi_qp_audio_math_diff(unsigned int freq, unsigned int n, 143 + unsigned int pixel_clk) 144 + { 145 + u64 cts = mul_u32_u32(pixel_clk, n); 146 + 147 + return do_div(cts, 128 * freq); 148 + } 149 + 150 + static unsigned int dw_hdmi_qp_compute_n(struct dw_hdmi_qp *hdmi, 151 + unsigned long pixel_clk, 152 + unsigned long freq) 153 + { 154 + unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); 155 + unsigned int max_n = (128 * freq) / 300; 156 + unsigned int ideal_n = (128 * freq) / 1000; 157 + unsigned int best_n_distance = ideal_n; 158 + unsigned int best_n = 0; 159 + u64 best_diff = U64_MAX; 160 + int n; 161 + 162 + /* If the ideal N could satisfy the audio math, then just take it */ 163 + if (dw_hdmi_qp_audio_math_diff(freq, ideal_n, pixel_clk) == 0) 164 + return ideal_n; 165 + 166 + for (n = min_n; n <= max_n; n++) { 167 + u64 diff = dw_hdmi_qp_audio_math_diff(freq, n, pixel_clk); 168 + 169 + if (diff < best_diff || 170 + (diff == best_diff && abs(n - ideal_n) < best_n_distance)) { 171 + best_n = n; 172 + best_diff = diff; 173 + best_n_distance = abs(best_n - ideal_n); 174 + } 175 + 176 + /* 177 + * The best N already satisfy the audio math, and also be 178 + * the closest value to ideal N, so just cut the loop. 179 + */ 180 + if (best_diff == 0 && (abs(n - ideal_n) > best_n_distance)) 181 + break; 182 + } 183 + 184 + return best_n; 185 + } 186 + 187 + static unsigned int dw_hdmi_qp_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, 188 + unsigned long sample_rate) 189 + { 190 + int n = dw_hdmi_qp_match_tmds_n_table(hdmi, pixel_clk, sample_rate); 191 + 192 + if (n > 0) 193 + return n; 194 + 195 + dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", 196 + pixel_clk); 197 + 198 + return dw_hdmi_qp_compute_n(hdmi, pixel_clk, sample_rate); 199 + } 200 + 201 + static unsigned int dw_hdmi_qp_find_cts(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, 202 + unsigned long sample_rate) 203 + { 204 + const struct dw_hdmi_audio_tmds_cts *tmds_cts = NULL; 205 + int i; 206 + 207 + for (i = 0; common_tmds_cts_table[i].tmds != 0; i++) { 208 + if (pixel_clk == common_tmds_cts_table[i].tmds) { 209 + tmds_cts = &common_tmds_cts_table[i]; 210 + break; 211 + } 212 + } 213 + 214 + if (!tmds_cts) 215 + return 0; 216 + 217 + switch (sample_rate) { 218 + case 32000: 219 + return tmds_cts->cts_32k; 220 + case 44100: 221 + case 88200: 222 + case 176400: 223 + return tmds_cts->cts_44k1; 224 + case 48000: 225 + case 96000: 226 + case 192000: 227 + return tmds_cts->cts_48k; 228 + default: 229 + return -ENOENT; 230 + } 231 + } 232 + 233 + static void dw_hdmi_qp_set_audio_interface(struct dw_hdmi_qp *hdmi, 234 + struct hdmi_codec_daifmt *fmt, 235 + struct hdmi_codec_params *hparms) 236 + { 237 + u32 conf0 = 0; 238 + 239 + /* Reset the audio data path of the AVP */ 240 + dw_hdmi_qp_write(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST); 241 + 242 + /* Disable AUDS, ACR, AUDI */ 243 + dw_hdmi_qp_mod(hdmi, 0, 244 + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | PKTSCHED_AUDI_TX_EN, 245 + PKTSCHED_PKT_EN); 246 + 247 + /* Clear the audio FIFO */ 248 + dw_hdmi_qp_write(hdmi, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0); 249 + 250 + /* Select I2S interface as the audio source */ 251 + dw_hdmi_qp_mod(hdmi, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0); 252 + 253 + /* Enable the active i2s lanes */ 254 + switch (hparms->channels) { 255 + case 7 ... 8: 256 + conf0 |= I2S_LINES_EN(3); 257 + fallthrough; 258 + case 5 ... 6: 259 + conf0 |= I2S_LINES_EN(2); 260 + fallthrough; 261 + case 3 ... 4: 262 + conf0 |= I2S_LINES_EN(1); 263 + fallthrough; 264 + default: 265 + conf0 |= I2S_LINES_EN(0); 266 + break; 267 + } 268 + 269 + dw_hdmi_qp_mod(hdmi, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0); 270 + 271 + /* 272 + * Enable bpcuv generated internally for L-PCM, or received 273 + * from stream for NLPCM/HBR. 274 + */ 275 + switch (fmt->bit_fmt) { 276 + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: 277 + conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP; 278 + conf0 |= I2S_BPCUV_RCV_EN; 279 + break; 280 + default: 281 + conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS; 282 + break; 283 + } 284 + 285 + dw_hdmi_qp_mod(hdmi, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK, 286 + AUDIO_INTERFACE_CONFIG0); 287 + 288 + /* Enable audio FIFO auto clear when overflow */ 289 + dw_hdmi_qp_mod(hdmi, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK, 290 + AUDIO_INTERFACE_CONFIG0); 291 + } 292 + 293 + /* 294 + * When transmitting IEC60958 linear PCM audio, these registers allow to 295 + * configure the channel status information of all the channel status 296 + * bits in the IEC60958 frame. For the moment this configuration is only 297 + * used when the I2S audio interface, General Purpose Audio (GPA), 298 + * or AHB audio DMA (AHBAUDDMA) interface is active 299 + * (for S/PDIF interface this information comes from the stream). 300 + */ 301 + static void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, 302 + u8 *channel_status, bool ref2stream) 303 + { 304 + /* 305 + * AUDPKT_CHSTATUS_OVR0: { RSV, RSV, CS1, CS0 } 306 + * AUDPKT_CHSTATUS_OVR1: { CS6, CS5, CS4, CS3 } 307 + * 308 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 309 + * CS0: | Mode | d | c | b | a | 310 + * CS1: | Category Code | 311 + * CS2: | Channel Number | Source Number | 312 + * CS3: | Clock Accuracy | Sample Freq | 313 + * CS4: | Ori Sample Freq | Word Length | 314 + * CS5: | | CGMS-A | 315 + * CS6~CS23: Reserved 316 + * 317 + * a: use of channel status block 318 + * b: linear PCM identification: 0 for lpcm, 1 for nlpcm 319 + * c: copyright information 320 + * d: additional format information 321 + */ 322 + 323 + if (ref2stream) 324 + channel_status[0] |= IEC958_AES0_NONAUDIO; 325 + 326 + if ((dw_hdmi_qp_read(hdmi, AUDIO_INTERFACE_CONFIG0) & GENMASK(25, 24)) == AUD_HBR) { 327 + /* fixup cs for HBR */ 328 + channel_status[3] = (channel_status[3] & 0xf0) | IEC958_AES3_CON_FS_768000; 329 + channel_status[4] = (channel_status[4] & 0x0f) | IEC958_AES4_CON_ORIGFS_NOTID; 330 + } 331 + 332 + dw_hdmi_qp_write(hdmi, channel_status[0] | (channel_status[1] << 8), 333 + AUDPKT_CHSTATUS_OVR0); 334 + 335 + regmap_bulk_write(hdmi->regm, AUDPKT_CHSTATUS_OVR1, &channel_status[3], 1); 336 + 337 + if (ref2stream) 338 + dw_hdmi_qp_mod(hdmi, 0, 339 + AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, 340 + AUDPKT_CONTROL0); 341 + else 342 + dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, 343 + AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, 344 + AUDPKT_CONTROL0); 345 + } 346 + 347 + static void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned long long tmds_char_rate, 348 + unsigned int sample_rate) 349 + { 350 + unsigned int n, cts; 351 + 352 + n = dw_hdmi_qp_find_n(hdmi, tmds_char_rate, sample_rate); 353 + cts = dw_hdmi_qp_find_cts(hdmi, tmds_char_rate, sample_rate); 354 + 355 + dw_hdmi_qp_set_cts_n(hdmi, cts, n); 356 + } 357 + 358 + static int dw_hdmi_qp_audio_enable(struct drm_connector *connector, 359 + struct drm_bridge *bridge) 360 + { 361 + struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); 362 + 363 + if (hdmi->tmds_char_rate) 364 + dw_hdmi_qp_mod(hdmi, 0, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); 365 + 366 + return 0; 367 + } 368 + 369 + static int dw_hdmi_qp_audio_prepare(struct drm_connector *connector, 370 + struct drm_bridge *bridge, 371 + struct hdmi_codec_daifmt *fmt, 372 + struct hdmi_codec_params *hparms) 373 + { 374 + struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); 375 + bool ref2stream = false; 376 + 377 + if (!hdmi->tmds_char_rate) 378 + return -ENODEV; 379 + 380 + if (fmt->bit_clk_provider | fmt->frame_clk_provider) { 381 + dev_err(hdmi->dev, "unsupported clock settings\n"); 382 + return -EINVAL; 383 + } 384 + 385 + if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) 386 + ref2stream = true; 387 + 388 + dw_hdmi_qp_set_audio_interface(hdmi, fmt, hparms); 389 + dw_hdmi_qp_set_sample_rate(hdmi, hdmi->tmds_char_rate, hparms->sample_rate); 390 + dw_hdmi_qp_set_channel_status(hdmi, hparms->iec.status, ref2stream); 391 + drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, &hparms->cea); 392 + 393 + return 0; 394 + } 395 + 396 + static void dw_hdmi_qp_audio_disable_regs(struct dw_hdmi_qp *hdmi) 397 + { 398 + /* 399 + * Keep ACR, AUDI, AUDS packet always on to make SINK device 400 + * active for better compatibility and user experience. 401 + * 402 + * This also fix POP sound on some SINK devices which wakeup 403 + * from suspend to active. 404 + */ 405 + dw_hdmi_qp_mod(hdmi, I2S_BPCUV_RCV_DIS, I2S_BPCUV_RCV_MSK, 406 + AUDIO_INTERFACE_CONFIG0); 407 + dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, 408 + AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, 409 + AUDPKT_CONTROL0); 410 + 411 + dw_hdmi_qp_mod(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, 412 + AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); 413 + } 414 + 415 + static void dw_hdmi_qp_audio_disable(struct drm_connector *connector, 416 + struct drm_bridge *bridge) 417 + { 418 + struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); 419 + 420 + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector); 421 + 422 + if (hdmi->tmds_char_rate) 423 + dw_hdmi_qp_audio_disable_regs(hdmi); 168 424 } 169 425 170 426 static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi, ··· 785 361 return 0; 786 362 } 787 363 364 + /* 365 + * Static values documented in the TRM 366 + * Different values are only used for debug purposes 367 + */ 368 + #define DW_HDMI_QP_AUDIO_INFOFRAME_HB1 0x1 369 + #define DW_HDMI_QP_AUDIO_INFOFRAME_HB2 0xa 370 + 371 + static int dw_hdmi_qp_config_audio_infoframe(struct dw_hdmi_qp *hdmi, 372 + const u8 *buffer, size_t len) 373 + { 374 + /* 375 + * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } 376 + * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } 377 + * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } 378 + * 379 + * PB0: CheckSum 380 + * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | 381 + * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | 382 + * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | 383 + * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | 384 + * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | 385 + * PB6~PB10: Reserved 386 + * 387 + * AUDI_CONTENTS0 default value defined by HDMI specification, 388 + * and shall only be changed for debug purposes. 389 + */ 390 + u32 header_bytes = (DW_HDMI_QP_AUDIO_INFOFRAME_HB1 << 8) | 391 + (DW_HDMI_QP_AUDIO_INFOFRAME_HB2 << 16); 392 + 393 + regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS0, &header_bytes, 1); 394 + regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &buffer[3], 1); 395 + regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS2, &buffer[4], 1); 396 + 397 + /* Enable ACR, AUDI, AMD */ 398 + dw_hdmi_qp_mod(hdmi, 399 + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, 400 + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, 401 + PKTSCHED_PKT_EN); 402 + 403 + /* Enable AUDS */ 404 + dw_hdmi_qp_mod(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); 405 + 406 + return 0; 407 + } 408 + 788 409 static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, 789 410 struct drm_atomic_state *state) 790 411 { ··· 850 381 dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n", 851 382 __func__, conn_state->hdmi.tmds_char_rate); 852 383 op_mode = 0; 384 + hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; 853 385 } else { 854 386 dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); 855 387 op_mode = OPMODE_DVI; ··· 868 398 struct drm_atomic_state *state) 869 399 { 870 400 struct dw_hdmi_qp *hdmi = bridge->driver_private; 401 + 402 + hdmi->tmds_char_rate = 0; 871 403 872 404 hdmi->phy.ops->disable(hdmi, hdmi->phy.data); 873 405 } ··· 926 454 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); 927 455 break; 928 456 457 + case HDMI_INFOFRAME_TYPE_AUDIO: 458 + dw_hdmi_qp_mod(hdmi, 0, 459 + PKTSCHED_ACR_TX_EN | 460 + PKTSCHED_AUDS_TX_EN | 461 + PKTSCHED_AUDI_TX_EN, 462 + PKTSCHED_PKT_EN); 463 + break; 929 464 default: 930 465 dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); 931 466 } ··· 955 476 case HDMI_INFOFRAME_TYPE_DRM: 956 477 return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len); 957 478 479 + case HDMI_INFOFRAME_TYPE_AUDIO: 480 + return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len); 481 + 958 482 default: 959 483 dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); 960 484 return 0; ··· 975 493 .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid, 976 494 .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe, 977 495 .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe, 496 + .hdmi_audio_startup = dw_hdmi_qp_audio_enable, 497 + .hdmi_audio_shutdown = dw_hdmi_qp_audio_disable, 498 + .hdmi_audio_prepare = dw_hdmi_qp_audio_prepare, 978 499 }; 979 500 980 501 static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) ··· 1086 601 hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi); 1087 602 if (IS_ERR(hdmi->bridge.ddc)) 1088 603 return ERR_CAST(hdmi->bridge.ddc); 604 + 605 + hdmi->bridge.hdmi_audio_max_i2s_playback_channels = 8; 606 + hdmi->bridge.hdmi_audio_dev = dev; 607 + hdmi->bridge.hdmi_audio_dai_port = 1; 1089 608 1090 609 ret = devm_drm_bridge_add(dev, &hdmi->bridge); 1091 610 if (ret)