mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
drm/bridge: lontium-lt9611uxc: switch to HDMI audio helpers
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. Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org> Link: https://patch.msgid.link/20250803-lt9611uxc-hdmi-v1-2-cb9ce1793acf@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
This commit is contained in:
@@ -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 @@ retry:
|
||||
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 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user