File Index
Files and directories
Sign in
FILE

dp_audio.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/msm/dp/dp_audio.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  4 */
  5
  6
  7#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
  8
  9#include <linux/platform_device.h>
 10
 11#include <drm/display/drm_dp_helper.h>
 12#include <drm/drm_edid.h>
 13
 14#include "dp_audio.h"
 15#include "dp_drm.h"
 16#include "dp_panel.h"
 17#include "dp_reg.h"
 18#include "dp_display.h"
 19#include "dp_utils.h"
 20
 21struct msm_dp_audio_private {
 22	struct platform_device *pdev;
 23	struct drm_device *drm_dev;
 24	void __iomem *link_base;
 25
 26	u32 channels;
 27
 28	struct msm_dp_audio msm_dp_audio;
 29};
 30
 31static inline u32 msm_dp_read_link(struct msm_dp_audio_private *audio, u32 offset)
 32{
 33	return readl_relaxed(audio->link_base + offset);
 34}
 35
 36static inline void msm_dp_write_link(struct msm_dp_audio_private *audio,
 37			       u32 offset, u32 data)
 38{
 39	/*
 40	 * To make sure link reg writes happens before any other operation,
 41	 * this function uses writel() instread of writel_relaxed()
 42	 */
 43	writel(data, audio->link_base + offset);
 44}
 45
 46static void msm_dp_audio_stream_sdp(struct msm_dp_audio_private *audio)
 47{
 48	struct dp_sdp_header sdp_hdr = {
 49		.HB0 = 0x00,
 50		.HB1 = 0x02,
 51		.HB2 = 0x00,
 52		.HB3 = audio->channels - 1,
 53	};
 54	u32 header[2];
 55
 56	msm_dp_utils_pack_sdp_header(&sdp_hdr, header);
 57
 58	msm_dp_write_link(audio, MMSS_DP_AUDIO_STREAM_0, header[0]);
 59	msm_dp_write_link(audio, MMSS_DP_AUDIO_STREAM_1, header[1]);
 60}
 61
 62static void msm_dp_audio_timestamp_sdp(struct msm_dp_audio_private *audio)
 63{
 64	struct dp_sdp_header sdp_hdr = {
 65		.HB0 = 0x00,
 66		.HB1 = 0x01,
 67		.HB2 = 0x17,
 68		.HB3 = 0x0 | (0x11 << 2),
 69	};
 70	u32 header[2];
 71
 72	msm_dp_utils_pack_sdp_header(&sdp_hdr, header);
 73
 74	msm_dp_write_link(audio, MMSS_DP_AUDIO_TIMESTAMP_0, header[0]);
 75	msm_dp_write_link(audio, MMSS_DP_AUDIO_TIMESTAMP_1, header[1]);
 76}
 77
 78static void msm_dp_audio_infoframe_sdp(struct msm_dp_audio_private *audio)
 79{
 80	struct dp_sdp_header sdp_hdr = {
 81		.HB0 = 0x00,
 82		.HB1 = 0x84,
 83		.HB2 = 0x1b,
 84		.HB3 = 0x0 | (0x11 << 2),
 85	};
 86	u32 header[2];
 87
 88	msm_dp_utils_pack_sdp_header(&sdp_hdr, header);
 89
 90	msm_dp_write_link(audio, MMSS_DP_AUDIO_INFOFRAME_0, header[0]);
 91	msm_dp_write_link(audio, MMSS_DP_AUDIO_INFOFRAME_1, header[1]);
 92}
 93
 94static void msm_dp_audio_copy_management_sdp(struct msm_dp_audio_private *audio)
 95{
 96	struct dp_sdp_header sdp_hdr = {
 97		.HB0 = 0x00,
 98		.HB1 = 0x05,
 99		.HB2 = 0x0f,
100		.HB3 = 0x00,
101	};
102	u32 header[2];
103
104	msm_dp_utils_pack_sdp_header(&sdp_hdr, header);
105
106	msm_dp_write_link(audio, MMSS_DP_AUDIO_COPYMANAGEMENT_0, header[0]);
107	msm_dp_write_link(audio, MMSS_DP_AUDIO_COPYMANAGEMENT_1, header[1]);
108}
109
110static void msm_dp_audio_isrc_sdp(struct msm_dp_audio_private *audio)
111{
112	struct dp_sdp_header sdp_hdr = {
113		.HB0 = 0x00,
114		.HB1 = 0x06,
115		.HB2 = 0x0f,
116		.HB3 = 0x00,
117	};
118	u32 header[2];
119	u32 reg;
120
121	/* XXX: is it necessary to preserve this field? */
122	reg = msm_dp_read_link(audio, MMSS_DP_AUDIO_ISRC_1);
123	sdp_hdr.HB3 = FIELD_GET(HEADER_3_MASK, reg);
124
125	msm_dp_utils_pack_sdp_header(&sdp_hdr, header);
126
127	msm_dp_write_link(audio, MMSS_DP_AUDIO_ISRC_0, header[0]);
128	msm_dp_write_link(audio, MMSS_DP_AUDIO_ISRC_1, header[1]);
129}
130
131static void msm_dp_audio_config_sdp(struct msm_dp_audio_private *audio)
132{
133	u32 sdp_cfg, sdp_cfg2;
134
135	sdp_cfg = msm_dp_read_link(audio, MMSS_DP_SDP_CFG);
136	/* AUDIO_TIMESTAMP_SDP_EN */
137	sdp_cfg |= BIT(1);
138	/* AUDIO_STREAM_SDP_EN */
139	sdp_cfg |= BIT(2);
140	/* AUDIO_COPY_MANAGEMENT_SDP_EN */
141	sdp_cfg |= BIT(5);
142	/* AUDIO_ISRC_SDP_EN  */
143	sdp_cfg |= BIT(6);
144	/* AUDIO_INFOFRAME_SDP_EN  */
145	sdp_cfg |= BIT(20);
146
147	drm_dbg_dp(audio->drm_dev, "sdp_cfg = 0x%x\n", sdp_cfg);
148
149	msm_dp_write_link(audio, MMSS_DP_SDP_CFG, sdp_cfg);
150
151	sdp_cfg2 = msm_dp_read_link(audio, MMSS_DP_SDP_CFG2);
152	/* IFRM_REGSRC -> Do not use reg values */
153	sdp_cfg2 &= ~BIT(0);
154	/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
155	sdp_cfg2 &= ~BIT(1);
156
157	drm_dbg_dp(audio->drm_dev, "sdp_cfg2 = 0x%x\n", sdp_cfg2);
158
159	msm_dp_write_link(audio, MMSS_DP_SDP_CFG2, sdp_cfg2);
160}
161
162static void msm_dp_audio_setup_sdp(struct msm_dp_audio_private *audio)
163{
164	msm_dp_audio_config_sdp(audio);
165
166	msm_dp_audio_stream_sdp(audio);
167	msm_dp_audio_timestamp_sdp(audio);
168	msm_dp_audio_infoframe_sdp(audio);
169	msm_dp_audio_copy_management_sdp(audio);
170	msm_dp_audio_isrc_sdp(audio);
171}
172
173static void msm_dp_audio_setup_acr(struct msm_dp_audio_private *audio)
174{
175	u32 select, acr_ctrl;
176
177	switch (audio->msm_dp_audio.bw_code) {
178	case DP_LINK_BW_1_62:
179		select = 0;
180		break;
181	case DP_LINK_BW_2_7:
182		select = 1;
183		break;
184	case DP_LINK_BW_5_4:
185		select = 2;
186		break;
187	case DP_LINK_BW_8_1:
188		select = 3;
189		break;
190	default:
191		drm_dbg_dp(audio->drm_dev, "Unknown link rate\n");
192		select = 0;
193		break;
194	}
195
196	acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
197
198	drm_dbg_dp(audio->drm_dev, "select: %#x, acr_ctrl: %#x\n",
199		   select, acr_ctrl);
200
201	msm_dp_write_link(audio, MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
202}
203
204static void msm_dp_audio_safe_to_exit_level(struct msm_dp_audio_private *audio)
205{
206	u32 safe_to_exit_level, mainlink_levels;
207
208	switch (audio->msm_dp_audio.lane_count) {
209	case 1:
210		safe_to_exit_level = 14;
211		break;
212	case 2:
213		safe_to_exit_level = 8;
214		break;
215	case 4:
216		safe_to_exit_level = 5;
217		break;
218	default:
219		safe_to_exit_level = 14;
220		drm_dbg_dp(audio->drm_dev,
221				"setting the default safe_to_exit_level = %u\n",
222				safe_to_exit_level);
223		break;
224	}
225
226	mainlink_levels = msm_dp_read_link(audio, REG_DP_MAINLINK_LEVELS);
227	mainlink_levels &= 0xFE0;
228	mainlink_levels |= safe_to_exit_level;
229
230	drm_dbg_dp(audio->drm_dev,
231		   "mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
232		   mainlink_levels, safe_to_exit_level);
233
234	msm_dp_write_link(audio, REG_DP_MAINLINK_LEVELS, mainlink_levels);
235}
236
237static void msm_dp_audio_enable(struct msm_dp_audio_private *audio, bool enable)
238{
239	u32 audio_ctrl;
240
241	audio_ctrl = msm_dp_read_link(audio, MMSS_DP_AUDIO_CFG);
242
243	if (enable)
244		audio_ctrl |= BIT(0);
245	else
246		audio_ctrl &= ~BIT(0);
247
248	drm_dbg_dp(audio->drm_dev, "dp_audio_cfg = 0x%x\n", audio_ctrl);
249
250	msm_dp_write_link(audio, MMSS_DP_AUDIO_CFG, audio_ctrl);
251	/* make sure audio engine is disabled */
252	wmb();
253}
254
255static struct msm_dp_audio_private *msm_dp_audio_get_data(struct msm_dp *msm_dp_display)
256{
257	struct msm_dp_audio *msm_dp_audio;
258
259	msm_dp_audio = msm_dp_display->msm_dp_audio;
260	if (!msm_dp_audio) {
261		DRM_ERROR("invalid msm_dp_audio data\n");
262		return ERR_PTR(-EINVAL);
263	}
264
265	return container_of(msm_dp_audio, struct msm_dp_audio_private, msm_dp_audio);
266}
267
268int msm_dp_audio_prepare(struct drm_bridge *bridge,
269			 struct drm_connector *connector,
270			 struct hdmi_codec_daifmt *daifmt,
271			 struct hdmi_codec_params *params)
272{
273	int rc = 0;
274	struct msm_dp_audio_private *audio;
275	struct msm_dp *msm_dp_display;
276
277	msm_dp_display = to_dp_bridge(bridge)->msm_dp_display;
278
279	/*
280	 * there could be cases where sound card can be opened even
281	 * before OR even when DP is not connected . This can cause
282	 * unclocked access as the audio subsystem relies on the DP
283	 * driver to maintain the correct state of clocks. To protect
284	 * such cases check for connection status and bail out if not
285	 * connected.
286	 */
287	if (!msm_dp_display->power_on) {
288		rc = -EINVAL;
289		goto end;
290	}
291
292	audio = msm_dp_audio_get_data(msm_dp_display);
293	if (IS_ERR(audio)) {
294		rc = PTR_ERR(audio);
295		goto end;
296	}
297
298	audio->channels = params->channels;
299
300	msm_dp_audio_setup_sdp(audio);
301	msm_dp_audio_setup_acr(audio);
302	msm_dp_audio_safe_to_exit_level(audio);
303	msm_dp_audio_enable(audio, true);
304	msm_dp_display_signal_audio_start(msm_dp_display);
305	msm_dp_display->audio_enabled = true;
306
307end:
308	return rc;
309}
310
311void msm_dp_audio_shutdown(struct drm_bridge *bridge,
312			   struct drm_connector *connecter)
313{
314	struct msm_dp_audio_private *audio;
315	struct msm_dp *msm_dp_display;
316
317	msm_dp_display = to_dp_bridge(bridge)->msm_dp_display;
318	audio = msm_dp_audio_get_data(msm_dp_display);
319	if (IS_ERR(audio)) {
320		DRM_ERROR("failed to get audio data\n");
321		return;
322	}
323
324	/*
325	 * if audio was not enabled there is no need
326	 * to execute the shutdown and we can bail out early.
327	 * This also makes sure that we dont cause an unclocked
328	 * access when audio subsystem calls this without DP being
329	 * connected. is_connected cannot be used here as its set
330	 * to false earlier than this call
331	 */
332	if (!msm_dp_display->audio_enabled)
333		return;
334
335	msm_dp_audio_enable(audio, false);
336	/* signal the dp display to safely shutdown clocks */
337	msm_dp_display_signal_audio_complete(msm_dp_display);
338}
339
340struct msm_dp_audio *msm_dp_audio_get(struct platform_device *pdev,
341			      void __iomem *link_base)
342{
343	int rc = 0;
344	struct msm_dp_audio_private *audio;
345	struct msm_dp_audio *msm_dp_audio;
346
347	if (!pdev) {
348		DRM_ERROR("invalid input\n");
349		rc = -EINVAL;
350		goto error;
351	}
352
353	audio = devm_kzalloc(&pdev->dev, sizeof(*audio), GFP_KERNEL);
354	if (!audio) {
355		rc = -ENOMEM;
356		goto error;
357	}
358
359	audio->pdev = pdev;
360	audio->link_base = link_base;
361
362	msm_dp_audio = &audio->msm_dp_audio;
363
364	return msm_dp_audio;
365error:
366	return ERR_PTR(rc);
367}
368
369void msm_dp_audio_put(struct msm_dp_audio *msm_dp_audio)
370{
371	struct msm_dp_audio_private *audio;
372
373	if (!msm_dp_audio)
374		return;
375
376	audio = container_of(msm_dp_audio, struct msm_dp_audio_private, msm_dp_audio);
377
378	devm_kfree(&audio->pdev->dev, audio);
379}
~/ Read-only file preview.