18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "dp_catalog.h" 158c2ecf20Sopenharmony_ci#include "dp_audio.h" 168c2ecf20Sopenharmony_ci#include "dp_panel.h" 178c2ecf20Sopenharmony_ci#include "dp_display.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define HEADER_BYTE_2_BIT 0 208c2ecf20Sopenharmony_ci#define PARITY_BYTE_2_BIT 8 218c2ecf20Sopenharmony_ci#define HEADER_BYTE_1_BIT 16 228c2ecf20Sopenharmony_ci#define PARITY_BYTE_1_BIT 24 238c2ecf20Sopenharmony_ci#define HEADER_BYTE_3_BIT 16 248c2ecf20Sopenharmony_ci#define PARITY_BYTE_3_BIT 24 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct dp_audio_private { 278c2ecf20Sopenharmony_ci struct platform_device *audio_pdev; 288c2ecf20Sopenharmony_ci struct platform_device *pdev; 298c2ecf20Sopenharmony_ci struct dp_catalog *catalog; 308c2ecf20Sopenharmony_ci struct dp_panel *panel; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci bool engine_on; 338c2ecf20Sopenharmony_ci u32 channels; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci struct dp_audio dp_audio; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u8 dp_audio_get_g0_value(u8 data) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci u8 c[4]; 418c2ecf20Sopenharmony_ci u8 g[4]; 428c2ecf20Sopenharmony_ci u8 ret_data = 0; 438c2ecf20Sopenharmony_ci u8 i; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 468c2ecf20Sopenharmony_ci c[i] = (data >> i) & 0x01; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci g[0] = c[3]; 498c2ecf20Sopenharmony_ci g[1] = c[0] ^ c[3]; 508c2ecf20Sopenharmony_ci g[2] = c[1]; 518c2ecf20Sopenharmony_ci g[3] = c[2]; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 548c2ecf20Sopenharmony_ci ret_data = ((g[i] & 0x01) << i) | ret_data; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return ret_data; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic u8 dp_audio_get_g1_value(u8 data) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u8 c[4]; 628c2ecf20Sopenharmony_ci u8 g[4]; 638c2ecf20Sopenharmony_ci u8 ret_data = 0; 648c2ecf20Sopenharmony_ci u8 i; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 678c2ecf20Sopenharmony_ci c[i] = (data >> i) & 0x01; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci g[0] = c[0] ^ c[3]; 708c2ecf20Sopenharmony_ci g[1] = c[0] ^ c[1] ^ c[3]; 718c2ecf20Sopenharmony_ci g[2] = c[1] ^ c[2]; 728c2ecf20Sopenharmony_ci g[3] = c[2] ^ c[3]; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 758c2ecf20Sopenharmony_ci ret_data = ((g[i] & 0x01) << i) | ret_data; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return ret_data; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic u8 dp_audio_calculate_parity(u32 data) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u8 x0 = 0; 838c2ecf20Sopenharmony_ci u8 x1 = 0; 848c2ecf20Sopenharmony_ci u8 ci = 0; 858c2ecf20Sopenharmony_ci u8 iData = 0; 868c2ecf20Sopenharmony_ci u8 i = 0; 878c2ecf20Sopenharmony_ci u8 parity_byte; 888c2ecf20Sopenharmony_ci u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci for (i = 0; i < num_byte; i++) { 918c2ecf20Sopenharmony_ci iData = (data >> i*4) & 0xF; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ci = iData ^ x1; 948c2ecf20Sopenharmony_ci x1 = x0 ^ dp_audio_get_g1_value(ci); 958c2ecf20Sopenharmony_ci x0 = dp_audio_get_g0_value(ci); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci parity_byte = x1 | (x0 << 4); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return parity_byte; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic u32 dp_audio_get_header(struct dp_catalog *catalog, 1048c2ecf20Sopenharmony_ci enum dp_catalog_audio_sdp_type sdp, 1058c2ecf20Sopenharmony_ci enum dp_catalog_audio_header_type header) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci catalog->sdp_type = sdp; 1088c2ecf20Sopenharmony_ci catalog->sdp_header = header; 1098c2ecf20Sopenharmony_ci dp_catalog_audio_get_header(catalog); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return catalog->audio_data; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void dp_audio_set_header(struct dp_catalog *catalog, 1158c2ecf20Sopenharmony_ci u32 data, 1168c2ecf20Sopenharmony_ci enum dp_catalog_audio_sdp_type sdp, 1178c2ecf20Sopenharmony_ci enum dp_catalog_audio_header_type header) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci catalog->sdp_type = sdp; 1208c2ecf20Sopenharmony_ci catalog->sdp_header = header; 1218c2ecf20Sopenharmony_ci catalog->audio_data = data; 1228c2ecf20Sopenharmony_ci dp_catalog_audio_set_header(catalog); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void dp_audio_stream_sdp(struct dp_audio_private *audio) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 1288c2ecf20Sopenharmony_ci u32 value, new_value; 1298c2ecf20Sopenharmony_ci u8 parity_byte; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Config header and parity byte 1 */ 1328c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 1338c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci new_value = 0x02; 1368c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 1378c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_1_BIT) 1388c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_1_BIT)); 1398c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n", 1408c2ecf20Sopenharmony_ci value, parity_byte); 1418c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 1428c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Config header and parity byte 2 */ 1458c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 1468c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2); 1478c2ecf20Sopenharmony_ci new_value = value; 1488c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 1498c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_2_BIT) 1508c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_2_BIT)); 1518c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n", 1528c2ecf20Sopenharmony_ci value, parity_byte); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 1558c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Config header and parity byte 3 */ 1588c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 1598c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci new_value = audio->channels - 1; 1628c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 1638c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_3_BIT) 1648c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_3_BIT)); 1658c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n", 1668c2ecf20Sopenharmony_ci value, parity_byte); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 1698c2ecf20Sopenharmony_ci DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void dp_audio_timestamp_sdp(struct dp_audio_private *audio) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 1758c2ecf20Sopenharmony_ci u32 value, new_value; 1768c2ecf20Sopenharmony_ci u8 parity_byte; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Config header and parity byte 1 */ 1798c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 1808c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci new_value = 0x1; 1838c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 1848c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_1_BIT) 1858c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_1_BIT)); 1868c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n", 1878c2ecf20Sopenharmony_ci value, parity_byte); 1888c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 1898c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Config header and parity byte 2 */ 1928c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 1938c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci new_value = 0x17; 1968c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 1978c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_2_BIT) 1988c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_2_BIT)); 1998c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n", 2008c2ecf20Sopenharmony_ci value, parity_byte); 2018c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2028c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Config header and parity byte 3 */ 2058c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2068c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci new_value = (0x0 | (0x11 << 2)); 2098c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2108c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_3_BIT) 2118c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_3_BIT)); 2128c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n", 2138c2ecf20Sopenharmony_ci value, parity_byte); 2148c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2158c2ecf20Sopenharmony_ci DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void dp_audio_infoframe_sdp(struct dp_audio_private *audio) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 2218c2ecf20Sopenharmony_ci u32 value, new_value; 2228c2ecf20Sopenharmony_ci u8 parity_byte; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Config header and parity byte 1 */ 2258c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2268c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci new_value = 0x84; 2298c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2308c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_1_BIT) 2318c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_1_BIT)); 2328c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n", 2338c2ecf20Sopenharmony_ci value, parity_byte); 2348c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2358c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Config header and parity byte 2 */ 2388c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2398c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci new_value = 0x1b; 2428c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2438c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_2_BIT) 2448c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_2_BIT)); 2458c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n", 2468c2ecf20Sopenharmony_ci value, parity_byte); 2478c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2488c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Config header and parity byte 3 */ 2518c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2528c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci new_value = (0x0 | (0x11 << 2)); 2558c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2568c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_3_BIT) 2578c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_3_BIT)); 2588c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n", 2598c2ecf20Sopenharmony_ci new_value, parity_byte); 2608c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2618c2ecf20Sopenharmony_ci DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void dp_audio_copy_management_sdp(struct dp_audio_private *audio) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 2678c2ecf20Sopenharmony_ci u32 value, new_value; 2688c2ecf20Sopenharmony_ci u8 parity_byte; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Config header and parity byte 1 */ 2718c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2728c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci new_value = 0x05; 2758c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2768c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_1_BIT) 2778c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_1_BIT)); 2788c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n", 2798c2ecf20Sopenharmony_ci value, parity_byte); 2808c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2818c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Config header and parity byte 2 */ 2848c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2858c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci new_value = 0x0F; 2888c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 2898c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_2_BIT) 2908c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_2_BIT)); 2918c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n", 2928c2ecf20Sopenharmony_ci value, parity_byte); 2938c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 2948c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Config header and parity byte 3 */ 2978c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 2988c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci new_value = 0x0; 3018c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 3028c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_3_BIT) 3038c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_3_BIT)); 3048c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n", 3058c2ecf20Sopenharmony_ci value, parity_byte); 3068c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 3078c2ecf20Sopenharmony_ci DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void dp_audio_isrc_sdp(struct dp_audio_private *audio) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 3138c2ecf20Sopenharmony_ci u32 value, new_value; 3148c2ecf20Sopenharmony_ci u8 parity_byte; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Config header and parity byte 1 */ 3178c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 3188c2ecf20Sopenharmony_ci DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci new_value = 0x06; 3218c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 3228c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_1_BIT) 3238c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_1_BIT)); 3248c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n", 3258c2ecf20Sopenharmony_ci value, parity_byte); 3268c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 3278c2ecf20Sopenharmony_ci DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Config header and parity byte 2 */ 3308c2ecf20Sopenharmony_ci value = dp_audio_get_header(catalog, 3318c2ecf20Sopenharmony_ci DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci new_value = 0x0F; 3348c2ecf20Sopenharmony_ci parity_byte = dp_audio_calculate_parity(new_value); 3358c2ecf20Sopenharmony_ci value |= ((new_value << HEADER_BYTE_2_BIT) 3368c2ecf20Sopenharmony_ci | (parity_byte << PARITY_BYTE_2_BIT)); 3378c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n", 3388c2ecf20Sopenharmony_ci value, parity_byte); 3398c2ecf20Sopenharmony_ci dp_audio_set_header(catalog, value, 3408c2ecf20Sopenharmony_ci DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void dp_audio_setup_sdp(struct dp_audio_private *audio) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci dp_catalog_audio_config_sdp(audio->catalog); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dp_audio_stream_sdp(audio); 3488c2ecf20Sopenharmony_ci dp_audio_timestamp_sdp(audio); 3498c2ecf20Sopenharmony_ci dp_audio_infoframe_sdp(audio); 3508c2ecf20Sopenharmony_ci dp_audio_copy_management_sdp(audio); 3518c2ecf20Sopenharmony_ci dp_audio_isrc_sdp(audio); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void dp_audio_setup_acr(struct dp_audio_private *audio) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci u32 select = 0; 3578c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci switch (audio->dp_audio.bw_code) { 3608c2ecf20Sopenharmony_ci case DP_LINK_BW_1_62: 3618c2ecf20Sopenharmony_ci select = 0; 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci case DP_LINK_BW_2_7: 3648c2ecf20Sopenharmony_ci select = 1; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case DP_LINK_BW_5_4: 3678c2ecf20Sopenharmony_ci select = 2; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case DP_LINK_BW_8_1: 3708c2ecf20Sopenharmony_ci select = 3; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci default: 3738c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Unknown link rate\n"); 3748c2ecf20Sopenharmony_ci select = 0; 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci catalog->audio_data = select; 3798c2ecf20Sopenharmony_ci dp_catalog_audio_config_acr(catalog); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void dp_audio_safe_to_exit_level(struct dp_audio_private *audio) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 3858c2ecf20Sopenharmony_ci u32 safe_to_exit_level = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci switch (audio->dp_audio.lane_count) { 3888c2ecf20Sopenharmony_ci case 1: 3898c2ecf20Sopenharmony_ci safe_to_exit_level = 14; 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci case 2: 3928c2ecf20Sopenharmony_ci safe_to_exit_level = 8; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case 4: 3958c2ecf20Sopenharmony_ci safe_to_exit_level = 5; 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci default: 3988c2ecf20Sopenharmony_ci DRM_DEBUG_DP("setting the default safe_to_exit_level = %u\n", 3998c2ecf20Sopenharmony_ci safe_to_exit_level); 4008c2ecf20Sopenharmony_ci safe_to_exit_level = 14; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci catalog->audio_data = safe_to_exit_level; 4058c2ecf20Sopenharmony_ci dp_catalog_audio_sfe_level(catalog); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void dp_audio_enable(struct dp_audio_private *audio, bool enable) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct dp_catalog *catalog = audio->catalog; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci catalog->audio_data = enable; 4138c2ecf20Sopenharmony_ci dp_catalog_audio_enable(catalog); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci audio->engine_on = enable; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct dp_audio *dp_audio; 4218c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!pdev) { 4248c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 4258c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci dp_display = platform_get_drvdata(pdev); 4298c2ecf20Sopenharmony_ci if (!dp_display) { 4308c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 4318c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dp_audio = dp_display->dp_audio; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!dp_audio) { 4378c2ecf20Sopenharmony_ci DRM_ERROR("invalid dp_audio data\n"); 4388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return container_of(dp_audio, struct dp_audio_private, dp_audio); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int dp_audio_hook_plugged_cb(struct device *dev, void *data, 4458c2ecf20Sopenharmony_ci hdmi_codec_plugged_cb fn, 4468c2ecf20Sopenharmony_ci struct device *codec_dev) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci struct platform_device *pdev; 4508c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci pdev = to_platform_device(dev); 4538c2ecf20Sopenharmony_ci if (!pdev) { 4548c2ecf20Sopenharmony_ci pr_err("invalid input\n"); 4558c2ecf20Sopenharmony_ci return -ENODEV; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci dp_display = platform_get_drvdata(pdev); 4598c2ecf20Sopenharmony_ci if (!dp_display) { 4608c2ecf20Sopenharmony_ci pr_err("invalid input\n"); 4618c2ecf20Sopenharmony_ci return -ENODEV; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return dp_display_set_plugged_cb(dp_display, fn, codec_dev); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int dp_audio_get_eld(struct device *dev, 4688c2ecf20Sopenharmony_ci void *data, uint8_t *buf, size_t len) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct platform_device *pdev; 4718c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pdev = to_platform_device(dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!pdev) { 4768c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 4778c2ecf20Sopenharmony_ci return -ENODEV; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dp_display = platform_get_drvdata(pdev); 4818c2ecf20Sopenharmony_ci if (!dp_display) { 4828c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 4838c2ecf20Sopenharmony_ci return -ENODEV; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci memcpy(buf, dp_display->connector->eld, 4878c2ecf20Sopenharmony_ci min(sizeof(dp_display->connector->eld), len)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciint dp_audio_hw_params(struct device *dev, 4938c2ecf20Sopenharmony_ci void *data, 4948c2ecf20Sopenharmony_ci struct hdmi_codec_daifmt *daifmt, 4958c2ecf20Sopenharmony_ci struct hdmi_codec_params *params) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci int rc = 0; 4988c2ecf20Sopenharmony_ci struct dp_audio_private *audio; 4998c2ecf20Sopenharmony_ci struct platform_device *pdev; 5008c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci pdev = to_platform_device(dev); 5038c2ecf20Sopenharmony_ci dp_display = platform_get_drvdata(pdev); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* 5068c2ecf20Sopenharmony_ci * there could be cases where sound card can be opened even 5078c2ecf20Sopenharmony_ci * before OR even when DP is not connected . This can cause 5088c2ecf20Sopenharmony_ci * unclocked access as the audio subsystem relies on the DP 5098c2ecf20Sopenharmony_ci * driver to maintain the correct state of clocks. To protect 5108c2ecf20Sopenharmony_ci * such cases check for connection status and bail out if not 5118c2ecf20Sopenharmony_ci * connected. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci if (!dp_display->power_on) { 5148c2ecf20Sopenharmony_ci rc = -EINVAL; 5158c2ecf20Sopenharmony_ci goto end; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci audio = dp_audio_get_data(pdev); 5198c2ecf20Sopenharmony_ci if (IS_ERR(audio)) { 5208c2ecf20Sopenharmony_ci rc = PTR_ERR(audio); 5218c2ecf20Sopenharmony_ci goto end; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci audio->channels = params->channels; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci dp_audio_setup_sdp(audio); 5278c2ecf20Sopenharmony_ci dp_audio_setup_acr(audio); 5288c2ecf20Sopenharmony_ci dp_audio_safe_to_exit_level(audio); 5298c2ecf20Sopenharmony_ci dp_audio_enable(audio, true); 5308c2ecf20Sopenharmony_ci dp_display_signal_audio_start(dp_display); 5318c2ecf20Sopenharmony_ci dp_display->audio_enabled = true; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciend: 5348c2ecf20Sopenharmony_ci return rc; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void dp_audio_shutdown(struct device *dev, void *data) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct dp_audio_private *audio; 5408c2ecf20Sopenharmony_ci struct platform_device *pdev; 5418c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci pdev = to_platform_device(dev); 5448c2ecf20Sopenharmony_ci dp_display = platform_get_drvdata(pdev); 5458c2ecf20Sopenharmony_ci audio = dp_audio_get_data(pdev); 5468c2ecf20Sopenharmony_ci if (IS_ERR(audio)) { 5478c2ecf20Sopenharmony_ci DRM_ERROR("failed to get audio data\n"); 5488c2ecf20Sopenharmony_ci return; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * if audio was not enabled there is no need 5538c2ecf20Sopenharmony_ci * to execute the shutdown and we can bail out early. 5548c2ecf20Sopenharmony_ci * This also makes sure that we dont cause an unclocked 5558c2ecf20Sopenharmony_ci * access when audio subsystem calls this without DP being 5568c2ecf20Sopenharmony_ci * connected. is_connected cannot be used here as its set 5578c2ecf20Sopenharmony_ci * to false earlier than this call 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (!dp_display->audio_enabled) 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci dp_audio_enable(audio, false); 5638c2ecf20Sopenharmony_ci /* signal the dp display to safely shutdown clocks */ 5648c2ecf20Sopenharmony_ci dp_display_signal_audio_complete(dp_display); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic const struct hdmi_codec_ops dp_audio_codec_ops = { 5688c2ecf20Sopenharmony_ci .hw_params = dp_audio_hw_params, 5698c2ecf20Sopenharmony_ci .audio_shutdown = dp_audio_shutdown, 5708c2ecf20Sopenharmony_ci .get_eld = dp_audio_get_eld, 5718c2ecf20Sopenharmony_ci .hook_plugged_cb = dp_audio_hook_plugged_cb, 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic struct hdmi_codec_pdata codec_data = { 5758c2ecf20Sopenharmony_ci .ops = &dp_audio_codec_ops, 5768c2ecf20Sopenharmony_ci .max_i2s_channels = 8, 5778c2ecf20Sopenharmony_ci .i2s = 1, 5788c2ecf20Sopenharmony_ci}; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_civoid dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct dp_audio_private *audio_priv; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci audio_priv = container_of(dp_audio, struct dp_audio_private, dp_audio); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (audio_priv->audio_pdev) { 5878c2ecf20Sopenharmony_ci platform_device_unregister(audio_priv->audio_pdev); 5888c2ecf20Sopenharmony_ci audio_priv->audio_pdev = NULL; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciint dp_register_audio_driver(struct device *dev, 5938c2ecf20Sopenharmony_ci struct dp_audio *dp_audio) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct dp_audio_private *audio_priv; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci audio_priv = container_of(dp_audio, 5988c2ecf20Sopenharmony_ci struct dp_audio_private, dp_audio); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci audio_priv->audio_pdev = platform_device_register_data(dev, 6018c2ecf20Sopenharmony_ci HDMI_CODEC_DRV_NAME, 6028c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, 6038c2ecf20Sopenharmony_ci &codec_data, 6048c2ecf20Sopenharmony_ci sizeof(codec_data)); 6058c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(audio_priv->audio_pdev); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistruct dp_audio *dp_audio_get(struct platform_device *pdev, 6098c2ecf20Sopenharmony_ci struct dp_panel *panel, 6108c2ecf20Sopenharmony_ci struct dp_catalog *catalog) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci int rc = 0; 6138c2ecf20Sopenharmony_ci struct dp_audio_private *audio; 6148c2ecf20Sopenharmony_ci struct dp_audio *dp_audio; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (!pdev || !panel || !catalog) { 6178c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 6188c2ecf20Sopenharmony_ci rc = -EINVAL; 6198c2ecf20Sopenharmony_ci goto error; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci audio = devm_kzalloc(&pdev->dev, sizeof(*audio), GFP_KERNEL); 6238c2ecf20Sopenharmony_ci if (!audio) { 6248c2ecf20Sopenharmony_ci rc = -ENOMEM; 6258c2ecf20Sopenharmony_ci goto error; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci audio->pdev = pdev; 6298c2ecf20Sopenharmony_ci audio->panel = panel; 6308c2ecf20Sopenharmony_ci audio->catalog = catalog; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci dp_audio = &audio->dp_audio; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci dp_catalog_audio_init(catalog); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return dp_audio; 6378c2ecf20Sopenharmony_cierror: 6388c2ecf20Sopenharmony_ci return ERR_PTR(rc); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_civoid dp_audio_put(struct dp_audio *dp_audio) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct dp_audio_private *audio; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!dp_audio) 6468c2ecf20Sopenharmony_ci return; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci audio = container_of(dp_audio, struct dp_audio_private, dp_audio); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci devm_kfree(&audio->pdev->dev, audio); 6518c2ecf20Sopenharmony_ci} 652