While LT9611UXC is a DSI-to-HDMI bridge, it implements all HDMI-related
functions internally, in the firmware, thus it doesn't make sense to
implement DRM_BRIDGE_OP_HDMI. However it is possible to implement
DRM_BRIDGE_OP_HDMI_AUDIO, streamlining HDMI audio plumbing (which
includes plugged notifications and ELD handling).
Implement corresponding callbacks and trigger EDID read /
drm_connector_hdmi_audio_plugged_notify() from the hpd_notify callback.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 125 +++++++++++------------------
1 file changed, 49 insertions(+), 76 deletions(-)
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 38fb8776c0f441ae433c60a7680aaa6501a8956e..11aab07d88df646a54fea287030a183eb823b26d 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -17,8 +17,6 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <sound/hdmi-codec.h>
-
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
@@ -27,6 +25,8 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_hdmi_audio_helper.h>
+
#define EDID_BLOCK_SIZE 128
#define EDID_NUM_BLOCKS 2
@@ -48,7 +48,6 @@ struct lt9611uxc {
struct device_node *dsi1_node;
struct mipi_dsi_device *dsi0;
struct mipi_dsi_device *dsi1;
- struct platform_device *audio_pdev;
struct gpio_desc *reset_gpio;
struct gpio_desc *enable_gpio;
@@ -429,12 +428,52 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid
return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc);
}
+static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ const struct drm_edid *drm_edid;
+
+ if (status == connector_status_disconnected) {
+ drm_connector_hdmi_audio_plugged_notify(connector, false);
+ drm_edid_connector_update(connector, NULL);
+ return;
+ }
+
+ drm_edid = lt9611uxc_bridge_edid_read(bridge, connector);
+ drm_edid_connector_update(connector, drm_edid);
+ drm_edid_free(drm_edid);
+
+ if (status == connector_status_connected)
+ drm_connector_hdmi_audio_plugged_notify(connector, true);
+}
+
+static int lt9611uxc_hdmi_audio_prepare(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ /*
+ * LT9611UXC will automatically detect rate and sample size, so no need
+ * to setup anything here.
+ */
+ return 0;
+}
+
+static void lt9611uxc_hdmi_audio_shutdown(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+}
+
static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
.attach = lt9611uxc_bridge_attach,
.mode_valid = lt9611uxc_bridge_mode_valid,
.mode_set = lt9611uxc_bridge_mode_set,
.detect = lt9611uxc_bridge_detect,
.edid_read = lt9611uxc_bridge_edid_read,
+ .hpd_notify = lt9611uxc_bridge_hpd_notify,
+ .hdmi_audio_prepare = lt9611uxc_hdmi_audio_prepare,
+ .hdmi_audio_shutdown = lt9611uxc_hdmi_audio_shutdown,
};
static int lt9611uxc_parse_dt(struct device *dev,
@@ -508,73 +547,6 @@ static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc)
return ret < 0 ? ret : rev;
}
-static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data,
- struct hdmi_codec_daifmt *fmt,
- struct hdmi_codec_params *hparms)
-{
- /*
- * LT9611UXC will automatically detect rate and sample size, so no need
- * to setup anything here.
- */
- return 0;
-}
-
-static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
-{
-}
-
-static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
- struct device_node *endpoint,
- void *data)
-{
- struct of_endpoint of_ep;
- int ret;
-
- ret = of_graph_parse_endpoint(endpoint, &of_ep);
- if (ret < 0)
- return ret;
-
- /*
- * HDMI sound should be located as reg = <2>
- * Then, it is sound port 0
- */
- if (of_ep.port == 2)
- return 0;
-
- return -EINVAL;
-}
-
-static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
- .hw_params = lt9611uxc_hdmi_hw_params,
- .audio_shutdown = lt9611uxc_audio_shutdown,
- .get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id,
-};
-
-static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc)
-{
- struct hdmi_codec_pdata codec_data = {
- .ops = <9611uxc_codec_ops,
- .max_i2s_channels = 2,
- .i2s = 1,
- .data = lt9611uxc,
- };
-
- lt9611uxc->audio_pdev =
- platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
- PLATFORM_DEVID_AUTO,
- &codec_data, sizeof(codec_data));
-
- return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev);
-}
-
-static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc)
-{
- if (lt9611uxc->audio_pdev) {
- platform_device_unregister(lt9611uxc->audio_pdev);
- lt9611uxc->audio_pdev = NULL;
- }
-}
-
#define LT9611UXC_FW_PAGE_SIZE 32
static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf)
{
@@ -858,11 +830,17 @@ static int lt9611uxc_probe(struct i2c_client *client)
i2c_set_clientdata(client, lt9611uxc);
lt9611uxc->bridge.of_node = client->dev.of_node;
- lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+ lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI_AUDIO;
if (lt9611uxc->hpd_supported)
lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD;
lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ lt9611uxc->bridge.hdmi_audio_dev = dev;
+ lt9611uxc->bridge.hdmi_audio_max_i2s_playback_channels = 2;
+ lt9611uxc->bridge.hdmi_audio_dai_port = 2;
+
drm_bridge_add(<9611uxc->bridge);
/* Attach primary DSI */
@@ -881,10 +859,6 @@ static int lt9611uxc_probe(struct i2c_client *client)
}
}
- ret = lt9611uxc_audio_init(dev, lt9611uxc);
- if (ret)
- goto err_remove_bridge;
-
return 0;
err_remove_bridge:
@@ -908,7 +882,6 @@ static void lt9611uxc_remove(struct i2c_client *client)
free_irq(client->irq, lt9611uxc);
cancel_work_sync(<9611uxc->work);
- lt9611uxc_audio_exit(lt9611uxc);
drm_bridge_remove(<9611uxc->bridge);
mutex_destroy(<9611uxc->ocm_lock);
--
2.39.5
On 03/08/2025 13:53, Dmitry Baryshkov wrote: > While LT9611UXC is a DSI-to-HDMI bridge, it implements all HDMI-related > functions internally, in the firmware, thus it doesn't make sense to > implement DRM_BRIDGE_OP_HDMI. However it is possible to implement > DRM_BRIDGE_OP_HDMI_AUDIO, streamlining HDMI audio plumbing (which > includes plugged notifications and ELD handling). > > Implement corresponding callbacks and trigger EDID read / > drm_connector_hdmi_audio_plugged_notify() from the hpd_notify callback. > > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> > --- > drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 125 +++++++++++------------------ > 1 file changed, 49 insertions(+), 76 deletions(-) > > diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c > index 38fb8776c0f441ae433c60a7680aaa6501a8956e..11aab07d88df646a54fea287030a183eb823b26d 100644 > --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c > +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c > @@ -17,8 +17,6 @@ > #include <linux/wait.h> > #include <linux/workqueue.h> > > -#include <sound/hdmi-codec.h> > - > #include <drm/drm_atomic_helper.h> > #include <drm/drm_bridge.h> > #include <drm/drm_edid.h> > @@ -27,6 +25,8 @@ > #include <drm/drm_print.h> > #include <drm/drm_probe_helper.h> > > +#include <drm/display/drm_hdmi_audio_helper.h> > + > #define EDID_BLOCK_SIZE 128 > #define EDID_NUM_BLOCKS 2 > > @@ -48,7 +48,6 @@ struct lt9611uxc { > struct device_node *dsi1_node; > struct mipi_dsi_device *dsi0; > struct mipi_dsi_device *dsi1; > - struct platform_device *audio_pdev; > > struct gpio_desc *reset_gpio; > struct gpio_desc *enable_gpio; > @@ -429,12 +428,52 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid > return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc); > } > > +static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge, > + struct drm_connector *connector, > + enum drm_connector_status status) > +{ > + const struct drm_edid *drm_edid; > + > + if (status == connector_status_disconnected) { > + drm_connector_hdmi_audio_plugged_notify(connector, false); > + drm_edid_connector_update(connector, NULL); > + return; > + } > + > + drm_edid = lt9611uxc_bridge_edid_read(bridge, connector); > + drm_edid_connector_update(connector, drm_edid); > + drm_edid_free(drm_edid); > + > + if (status == connector_status_connected) > + drm_connector_hdmi_audio_plugged_notify(connector, true); > +} > + > +static int lt9611uxc_hdmi_audio_prepare(struct drm_bridge *bridge, > + struct drm_connector *connector, > + struct hdmi_codec_daifmt *fmt, > + struct hdmi_codec_params *hparms) > +{ > + /* > + * LT9611UXC will automatically detect rate and sample size, so no need > + * to setup anything here. > + */ > + return 0; > +} > + > +static void lt9611uxc_hdmi_audio_shutdown(struct drm_bridge *bridge, > + struct drm_connector *connector) > +{ > +} > + > static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = { > .attach = lt9611uxc_bridge_attach, > .mode_valid = lt9611uxc_bridge_mode_valid, > .mode_set = lt9611uxc_bridge_mode_set, > .detect = lt9611uxc_bridge_detect, > .edid_read = lt9611uxc_bridge_edid_read, > + .hpd_notify = lt9611uxc_bridge_hpd_notify, > + .hdmi_audio_prepare = lt9611uxc_hdmi_audio_prepare, > + .hdmi_audio_shutdown = lt9611uxc_hdmi_audio_shutdown, > }; > > static int lt9611uxc_parse_dt(struct device *dev, > @@ -508,73 +547,6 @@ static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc) > return ret < 0 ? ret : rev; > } > > -static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data, > - struct hdmi_codec_daifmt *fmt, > - struct hdmi_codec_params *hparms) > -{ > - /* > - * LT9611UXC will automatically detect rate and sample size, so no need > - * to setup anything here. > - */ > - return 0; > -} > - > -static void lt9611uxc_audio_shutdown(struct device *dev, void *data) > -{ > -} > - > -static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component, > - struct device_node *endpoint, > - void *data) > -{ > - struct of_endpoint of_ep; > - int ret; > - > - ret = of_graph_parse_endpoint(endpoint, &of_ep); > - if (ret < 0) > - return ret; > - > - /* > - * HDMI sound should be located as reg = <2> > - * Then, it is sound port 0 > - */ > - if (of_ep.port == 2) > - return 0; > - > - return -EINVAL; > -} > - > -static const struct hdmi_codec_ops lt9611uxc_codec_ops = { > - .hw_params = lt9611uxc_hdmi_hw_params, > - .audio_shutdown = lt9611uxc_audio_shutdown, > - .get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id, > -}; > - > -static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc) > -{ > - struct hdmi_codec_pdata codec_data = { > - .ops = <9611uxc_codec_ops, > - .max_i2s_channels = 2, > - .i2s = 1, > - .data = lt9611uxc, > - }; > - > - lt9611uxc->audio_pdev = > - platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, > - PLATFORM_DEVID_AUTO, > - &codec_data, sizeof(codec_data)); > - > - return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev); > -} > - > -static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc) > -{ > - if (lt9611uxc->audio_pdev) { > - platform_device_unregister(lt9611uxc->audio_pdev); > - lt9611uxc->audio_pdev = NULL; > - } > -} > - > #define LT9611UXC_FW_PAGE_SIZE 32 > static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf) > { > @@ -858,11 +830,17 @@ static int lt9611uxc_probe(struct i2c_client *client) > i2c_set_clientdata(client, lt9611uxc); > > lt9611uxc->bridge.of_node = client->dev.of_node; > - lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; > + lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | > + DRM_BRIDGE_OP_EDID | > + DRM_BRIDGE_OP_HDMI_AUDIO; > if (lt9611uxc->hpd_supported) > lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD; > lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA; > > + lt9611uxc->bridge.hdmi_audio_dev = dev; > + lt9611uxc->bridge.hdmi_audio_max_i2s_playback_channels = 2; > + lt9611uxc->bridge.hdmi_audio_dai_port = 2; > + > drm_bridge_add(<9611uxc->bridge); > > /* Attach primary DSI */ > @@ -881,10 +859,6 @@ static int lt9611uxc_probe(struct i2c_client *client) > } > } > > - ret = lt9611uxc_audio_init(dev, lt9611uxc); > - if (ret) > - goto err_remove_bridge; > - > return 0; > > err_remove_bridge: > @@ -908,7 +882,6 @@ static void lt9611uxc_remove(struct i2c_client *client) > > free_irq(client->irq, lt9611uxc); > cancel_work_sync(<9611uxc->work); > - lt9611uxc_audio_exit(lt9611uxc); > drm_bridge_remove(<9611uxc->bridge); > > mutex_destroy(<9611uxc->ocm_lock); > LGTM Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
© 2016 - 2025 Red Hat, Inc.