mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Make the dp/hdmi_audio_* callback maintain the same parameter order as get_modes and edid_read: first the bridge, then the connector. Signed-off-by: Andy Yan <andy.yan@rock-chips.com> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Link: https://lore.kernel.org/r/20250703125027.311109-2-andyshrk@163.com [DB: added the chunk to the cdn-dp driver] Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
179 lines
4.9 KiB
C
179 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2013 Red Hat
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
*/
|
|
|
|
#include <drm/display/drm_hdmi_helper.h>
|
|
#include <drm/display/drm_hdmi_state_helper.h>
|
|
|
|
#include <linux/hdmi.h>
|
|
|
|
#include <sound/hdmi-codec.h>
|
|
|
|
#include "hdmi.h"
|
|
|
|
int msm_hdmi_audio_update(struct hdmi *hdmi)
|
|
{
|
|
struct hdmi_audio *audio = &hdmi->audio;
|
|
bool enabled = audio->enabled;
|
|
uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl;
|
|
uint32_t audio_config;
|
|
|
|
if (!hdmi->connector->display_info.is_hdmi)
|
|
return -EINVAL;
|
|
|
|
DBG("audio: enabled=%d, channels=%d, rate=%d",
|
|
audio->enabled, audio->channels, audio->rate);
|
|
|
|
DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock);
|
|
|
|
if (enabled && !(hdmi->power_on && hdmi->pixclock)) {
|
|
DBG("disabling audio: no video");
|
|
enabled = false;
|
|
}
|
|
|
|
/* Read first before writing */
|
|
acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL);
|
|
vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL);
|
|
aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1);
|
|
audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG);
|
|
|
|
/* Clear N/CTS selection bits */
|
|
acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SELECT__MASK;
|
|
|
|
if (enabled) {
|
|
uint32_t n, cts, multiplier;
|
|
enum hdmi_acr_cts select;
|
|
|
|
drm_hdmi_acr_get_n_cts(hdmi->pixclock, audio->rate, &n, &cts);
|
|
|
|
if (audio->rate == 192000 || audio->rate == 176400) {
|
|
multiplier = 4;
|
|
n >>= 2; /* divide N by 4 and use multiplier */
|
|
} else if (audio->rate == 96000 || audio->rate == 88200) {
|
|
multiplier = 2;
|
|
n >>= 1; /* divide N by 2 and use multiplier */
|
|
} else {
|
|
multiplier = 1;
|
|
}
|
|
|
|
DBG("n=%u, cts=%u, multiplier=%u", n, cts, multiplier);
|
|
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SOURCE;
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY;
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_N_MULTIPLIER(multiplier);
|
|
|
|
if (audio->rate == 48000 || audio->rate == 96000 ||
|
|
audio->rate == 192000)
|
|
select = ACR_48;
|
|
else if (audio->rate == 44100 || audio->rate == 88200 ||
|
|
audio->rate == 176400)
|
|
select = ACR_44;
|
|
else /* default to 32k */
|
|
select = ACR_32;
|
|
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SELECT(select);
|
|
|
|
hdmi_write(hdmi, REG_HDMI_ACR_0(select - 1),
|
|
HDMI_ACR_0_CTS(cts));
|
|
hdmi_write(hdmi, REG_HDMI_ACR_1(select - 1),
|
|
HDMI_ACR_1_N(n));
|
|
|
|
hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2,
|
|
COND(audio->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) |
|
|
HDMI_AUDIO_PKT_CTRL2_OVERRIDE);
|
|
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT;
|
|
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND;
|
|
|
|
hdmi_write(hdmi, REG_HDMI_GC, 0);
|
|
|
|
vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE;
|
|
vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME;
|
|
|
|
aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
|
|
|
|
audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK;
|
|
audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4);
|
|
audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE;
|
|
} else {
|
|
acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT;
|
|
acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND;
|
|
vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE;
|
|
vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME;
|
|
aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
|
|
audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE;
|
|
}
|
|
|
|
hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl);
|
|
hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl);
|
|
hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl);
|
|
|
|
hdmi_write(hdmi, REG_HDMI_AUD_INT,
|
|
COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) |
|
|
COND(enabled, HDMI_AUD_INT_AUD_SAM_DROP_INT));
|
|
|
|
hdmi_write(hdmi, REG_HDMI_AUDIO_CFG, audio_config);
|
|
|
|
|
|
DBG("audio %sabled", enabled ? "en" : "dis");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int msm_hdmi_bridge_audio_prepare(struct drm_bridge *bridge,
|
|
struct drm_connector *connector,
|
|
struct hdmi_codec_daifmt *daifmt,
|
|
struct hdmi_codec_params *params)
|
|
{
|
|
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
|
|
struct hdmi *hdmi = hdmi_bridge->hdmi;
|
|
int ret;
|
|
|
|
drm_dbg_driver(bridge->dev, "%u Hz, %d bit, %d channels\n",
|
|
params->sample_rate,
|
|
params->sample_width,
|
|
params->cea.channels);
|
|
|
|
switch (params->sample_rate) {
|
|
case 32000:
|
|
case 44100:
|
|
case 48000:
|
|
case 88200:
|
|
case 96000:
|
|
case 176400:
|
|
case 192000:
|
|
break;
|
|
default:
|
|
drm_err(bridge->dev, "rate[%d] not supported!\n",
|
|
params->sample_rate);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
|
|
¶ms->cea);
|
|
if (ret)
|
|
return ret;
|
|
|
|
hdmi->audio.rate = params->sample_rate;
|
|
hdmi->audio.channels = params->cea.channels;
|
|
hdmi->audio.enabled = true;
|
|
|
|
return msm_hdmi_audio_update(hdmi);
|
|
}
|
|
|
|
void msm_hdmi_bridge_audio_shutdown(struct drm_bridge *bridge,
|
|
struct drm_connector *connector)
|
|
{
|
|
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
|
|
struct hdmi *hdmi = hdmi_bridge->hdmi;
|
|
|
|
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
|
|
|
|
hdmi->audio.rate = 0;
|
|
hdmi->audio.channels = 2;
|
|
hdmi->audio.enabled = false;
|
|
|
|
msm_hdmi_audio_update(hdmi);
|
|
}
|