Skip to content

Commit

Permalink
drm/bridge: connector: add support for HDMI codec framework
Browse files Browse the repository at this point in the history
Add necessary glue code to be able to use new HDMI codec framework from
the DRM bridge drivers. The drm_bridge implements a limited set of the
hdmi_codec_ops interface, with the functions accepting both
drm_connector and drm_bridge instead of just a generic void pointer.

This framework is integrated with the DRM HDMI Connector framework, but
can also be used for DisplayPort connectors.

Reviewed-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20241224-drm-bridge-hdmi-connector-v10-4-dc89577cd438@linaro.org
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
  • Loading branch information
Dmitry Baryshkov committed Jan 4, 2025
1 parent baf6166 commit 0beba3f
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 4 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/display/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ if DRM_DISPLAY_HELPER

config DRM_BRIDGE_CONNECTOR
bool
select DRM_DISPLAY_HDMI_AUDIO_HELPER
select DRM_DISPLAY_HDMI_STATE_HELPER
help
DRM connector implementation terminating DRM bridge chains.
Expand Down
99 changes: 95 additions & 4 deletions drivers/gpu/drm/display/drm_bridge_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_probe_helper.h>
#include <drm/display/drm_hdmi_audio_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>

Expand Down Expand Up @@ -387,6 +388,75 @@ static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = {
.write_infoframe = drm_bridge_connector_write_infoframe,
};

static int drm_bridge_connector_audio_startup(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;

bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;

if (!bridge->funcs->hdmi_audio_startup)
return 0;

return bridge->funcs->hdmi_audio_startup(connector, bridge);
}

static int drm_bridge_connector_audio_prepare(struct drm_connector *connector,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;

bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;

return bridge->funcs->hdmi_audio_prepare(connector, bridge, fmt, hparms);
}

static void drm_bridge_connector_audio_shutdown(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;

bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return;

bridge->funcs->hdmi_audio_shutdown(connector, bridge);
}

static int drm_bridge_connector_audio_mute_stream(struct drm_connector *connector,
bool enable, int direction)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;

bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;

if (bridge->funcs->hdmi_audio_mute_stream)
return bridge->funcs->hdmi_audio_mute_stream(connector, bridge,
enable, direction);
else
return -ENOTSUPP;
}

static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_audio_funcs = {
.startup = drm_bridge_connector_audio_startup,
.prepare = drm_bridge_connector_audio_prepare,
.shutdown = drm_bridge_connector_audio_shutdown,
.mute_stream = drm_bridge_connector_audio_mute_stream,
};

/* -----------------------------------------------------------------------------
* Bridge Connector Initialisation
*/
Expand Down Expand Up @@ -486,7 +556,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
return ERR_PTR(-EINVAL);

if (bridge_connector->bridge_hdmi)
if (bridge_connector->bridge_hdmi) {
bridge = bridge_connector->bridge_hdmi;

ret = drmm_connector_hdmi_init(drm, connector,
bridge_connector->bridge_hdmi->vendor,
bridge_connector->bridge_hdmi->product,
Expand All @@ -495,12 +567,31 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
connector_type, ddc,
supported_formats,
max_bpc);
else
if (ret)
return ERR_PTR(ret);

if (bridge->hdmi_audio_max_i2s_playback_channels ||
bridge->hdmi_audio_spdif_playback) {
if (!bridge->funcs->hdmi_audio_prepare ||
!bridge->funcs->hdmi_audio_shutdown)
return ERR_PTR(-EINVAL);

ret = drm_connector_hdmi_audio_init(connector,
bridge->hdmi_audio_dev,
&drm_bridge_connector_hdmi_audio_funcs,
bridge->hdmi_audio_max_i2s_playback_channels,
bridge->hdmi_audio_spdif_playback,
bridge->hdmi_audio_dai_port);
if (ret)
return ERR_PTR(ret);
}
} else {
ret = drmm_connector_init(drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc);
if (ret)
return ERR_PTR(ret);
if (ret)
return ERR_PTR(ret);
}

drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);

Expand Down
74 changes: 74 additions & 0 deletions include/drm/drm_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ struct drm_display_info;
struct drm_minor;
struct drm_panel;
struct edid;
struct hdmi_codec_daifmt;
struct hdmi_codec_params;
struct i2c_adapter;

/**
Expand Down Expand Up @@ -676,6 +678,57 @@ struct drm_bridge_funcs {
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len);

/**
* @hdmi_audio_startup:
*
* Called when ASoC starts an audio stream setup. The
* @hdmi_audio_startup() is optional.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*hdmi_audio_startup)(struct drm_connector *connector,
struct drm_bridge *bridge);

/**
* @prepare:
* Configures HDMI-encoder for audio stream. Can be called multiple
* times for each setup. Mandatory if HDMI audio is enabled in the
* bridge's configuration.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*hdmi_audio_prepare)(struct drm_connector *connector,
struct drm_bridge *bridge,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);

/**
* @hdmi_audio_shutdown:
*
* Shut down the audio stream. Mandatory if HDMI audio is enabled in
* the bridge's configuration.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
void (*hdmi_audio_shutdown)(struct drm_connector *connector,
struct drm_bridge *bridge);

/**
* @hdmi_audio_mute_stream:
*
* Mute/unmute HDMI audio stream. The @hdmi_audio_mute_stream callback
* is optional.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*hdmi_audio_mute_stream)(struct drm_connector *connector,
struct drm_bridge *bridge,
bool enable, int direction);

/**
* @debugfs_init:
*
Expand Down Expand Up @@ -859,6 +912,27 @@ struct drm_bridge {
* @DRM_BRIDGE_OP_HDMI is set.
*/
unsigned int max_bpc;

/**
* @hdmi_audio_dev: device to be used as a parent for the HDMI Codec
*/
struct device *hdmi_audio_dev;

/**
* @hdmi_audio_max_i2s_playback_channels: maximum number of playback
* I2S channels for the HDMI codec
*/
int hdmi_audio_max_i2s_playback_channels;

/**
* @hdmi_audio_spdif_playback: set if HDMI codec has S/PDIF playback port
*/
unsigned int hdmi_audio_spdif_playback : 1;

/**
* @hdmi_audio_dai_port: sound DAI port, -1 if it is not enabled
*/
int hdmi_audio_dai_port;
};

static inline struct drm_bridge *
Expand Down

0 comments on commit 0beba3f

Please sign in to comment.