diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index eb9ec9f17828..dbcaedbdbeab 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1600,6 +1600,48 @@ static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, return analogix_dp_transfer(dp, msg); } +int analogix_dp_audio_hw_params(struct analogix_dp_device *dp, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + switch (daifmt->fmt) { + case HDMI_SPDIF: + analogix_dp_audio_config_spdif(dp); + break; + case HDMI_I2S: + analogix_dp_audio_config_i2s(dp); + break; + default: + DRM_DEV_ERROR(dp->dev, "invalid daifmt %d\n", daifmt->fmt); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_audio_hw_params); + +void analogix_dp_audio_shutdown(struct analogix_dp_device *dp) +{ + analogix_dp_audio_disable(dp); +} +EXPORT_SYMBOL_GPL(analogix_dp_audio_shutdown); + +int analogix_dp_audio_startup(struct analogix_dp_device *dp) +{ + analogix_dp_audio_enable(dp); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_audio_startup); + +int analogix_dp_audio_get_eld(struct analogix_dp_device *dp, u8 *buf, size_t len) +{ + memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len)); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_audio_get_eld); + struct analogix_dp_device * analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) { diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 3bed7b03bea9..de32a352383d 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -249,5 +249,9 @@ void analogix_dp_video_bist_enable(struct analogix_dp_device *dp); bool analogix_dp_ssc_supported(struct analogix_dp_device *dp); void analogix_dp_phy_power_on(struct analogix_dp_device *dp); void analogix_dp_phy_power_off(struct analogix_dp_device *dp); +void analogix_dp_audio_config_spdif(struct analogix_dp_device *dp); +void analogix_dp_audio_config_i2s(struct analogix_dp_device *dp); +void analogix_dp_audio_enable(struct analogix_dp_device *dp); +void analogix_dp_audio_disable(struct analogix_dp_device *dp); #endif /* _ANALOGIX_DP_CORE_H */ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index bc00f769f849..ca91db220580 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -1228,3 +1228,53 @@ void analogix_dp_video_bist_enable(struct analogix_dp_device *dp) reg &= ~FORMAT_SEL; analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg); } + +void analogix_dp_audio_config_i2s(struct analogix_dp_device *dp) +{ + u32 reg; + + reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4); + reg &= ~FIX_M_AUD; + analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg); + + reg = analogix_dp_read(dp, ANALOGIX_DP_I2S_CTRL); + reg |= I2S_EN; + analogix_dp_write(dp, ANALOGIX_DP_I2S_CTRL, reg); +} + +void analogix_dp_audio_config_spdif(struct analogix_dp_device *dp) +{ + u32 reg; + + reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4); + reg &= ~FIX_M_AUD; + analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg); + + reg = analogix_dp_read(dp, ANALOGIX_DP_SPDIF_AUDIO_CTL_0); + reg |= AUD_SPDIF_EN; + analogix_dp_write(dp, ANALOGIX_DP_SPDIF_AUDIO_CTL_0, reg); +} + +void analogix_dp_audio_enable(struct analogix_dp_device *dp) +{ + u32 reg; + + reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1); + reg &= ~(AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N); + analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg); + + reg = analogix_dp_read(dp, ANALOGIX_DP_AUD_CTL); + reg |= MISC_CTRL_RESET | DP_AUDIO_EN; + analogix_dp_write(dp, ANALOGIX_DP_AUD_CTL, reg); +} + +void analogix_dp_audio_disable(struct analogix_dp_device *dp) +{ + u32 reg; + + analogix_dp_write(dp, ANALOGIX_DP_AUD_CTL, 0); + + reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1); + reg |= AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N; + analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg); +} diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h index 81ce406d97b5..346024aedc8b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h @@ -88,7 +88,7 @@ #define ANALOGIX_DP_SYS_CTL_2 0x604 #define ANALOGIX_DP_SYS_CTL_3 0x608 #define ANALOGIX_DP_SYS_CTL_4 0x60C - +#define ANALOGIX_DP_AUD_CTL 0x618 #define ANALOGIX_DP_PKT_SEND_CTL 0x640 #define ANALOGIX_DP_HDCP_CTL 0x648 @@ -134,8 +134,9 @@ #define ANALOGIX_DP_BUF_DATA_0 0x7C0 #define ANALOGIX_DP_SOC_GENERAL_CTL 0x800 - +#define ANALOGIX_DP_AUD_CHANNEL_CTL 0x834 #define ANALOGIX_DP_CRC_CON 0x890 +#define ANALOGIX_DP_I2S_CTRL 0x9C8 /* ANALOGIX_DP_TX_SW_RESET */ #define RESET_DP_TX (0x1 << 0) @@ -255,6 +256,9 @@ /* ANALOGIX_DP_H_B_PORCH_CFG_H */ #define H_B_PORCH_CFG_H(x) (((x) & 0xf) << 0) +/* ANALOGIX_DP_SPDIF_AUDIO_CTL_0 */ +#define AUD_SPDIF_EN (0x1 << 7) + /* ANALOGIX_DP_PLL_REG_1 */ #define REF_CLK_24M (0x1 << 0) #define REF_CLK_27M (0x0 << 0) @@ -383,6 +387,10 @@ #define FIX_M_VID (0x1 << 2) #define M_VID_UPDATE_CTRL (0x3 << 0) +/* ANALOGIX_DP_AUD_CTL */ +#define MISC_CTRL_RESET (0x1 << 4) +#define DP_AUDIO_EN (0x1 << 0) + /* ANALOGIX_DP_TRAINING_PTN_SET */ #define SCRAMBLER_TYPE (0x1 << 9) #define HW_LINK_TRAINING_PATTERN (0x1 << 8) @@ -480,6 +488,11 @@ #define VIDEO_MODE_SLAVE_MODE (0x1 << 0) #define VIDEO_MODE_MASTER_MODE (0x0 << 0) +/* ANALOGIX_DP_AUD_CHANNEL_CTL */ +#define AUD_CHANNEL_COUNT_6 (0x5 << 0) +#define AUD_CHANNEL_COUNT_4 (0x3 << 0) +#define AUD_CHANNEL_COUNT_2 (0x1 << 0) + /* ANALOGIX_DP_PKT_SEND_CTL */ #define IF_UP (0x1 << 4) #define IF_EN (0x1 << 0) @@ -488,4 +501,7 @@ #define PSR_VID_CRC_FLUSH (0x1 << 2) #define PSR_VID_CRC_ENABLE (0x1 << 0) +/* ANALOGIX_DP_I2S_CTRL */ +#define I2S_EN (0x1 << 4) + #endif /* _ANALOGIX_DP_REG_H */ diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 4d5d14d85280..89bacc76d89d 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -50,6 +50,7 @@ * @lcdsel_lit: reg value of selecting vop little for eDP * @chip_type: specific chip type * @ssc: check if SSC is supported by source + * @audio: check if audio is supported by source */ struct rockchip_dp_chip_data { u32 lcdsel_grf_reg; @@ -57,6 +58,7 @@ struct rockchip_dp_chip_data { u32 lcdsel_lit; u32 chip_type; bool ssc; + bool audio; }; struct rockchip_dp_device { @@ -69,12 +71,51 @@ struct rockchip_dp_device { struct reset_control *rst; struct reset_control *apb_reset; + struct platform_device *audio_pdev; const struct rockchip_dp_chip_data *data; struct analogix_dp_device *adp; struct analogix_dp_plat_data plat_data; }; +static int rockchip_dp_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + return analogix_dp_audio_hw_params(dp->adp, daifmt, params); +} + +static void rockchip_dp_audio_shutdown(struct device *dev, void *data) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + analogix_dp_audio_shutdown(dp->adp); +} + +static int rockchip_dp_audio_startup(struct device *dev, void *data) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + return analogix_dp_audio_startup(dp->adp); +} + +static int rockchip_dp_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + return analogix_dp_audio_get_eld(dp->adp, buf, len); +} + +static const struct hdmi_codec_ops rockchip_dp_audio_codec_ops = { + .hw_params = rockchip_dp_audio_hw_params, + .audio_startup = rockchip_dp_audio_startup, + .audio_shutdown = rockchip_dp_audio_shutdown, + .get_eld = rockchip_dp_audio_get_eld, +}; + static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) { reset_control_assert(dp->rst); @@ -331,6 +372,25 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, if (ret) goto err_cleanup_encoder; + if (dp->data->audio) { + struct hdmi_codec_pdata codec_data = { + .ops = &rockchip_dp_audio_codec_ops, + .spdif = 1, + .i2s = 1, + .max_i2s_channels = 2, + }; + + dp->audio_pdev = + platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + if (IS_ERR(dp->audio_pdev)) { + ret = PTR_ERR(dp->audio_pdev); + goto err_cleanup_encoder; + } + } + return 0; err_cleanup_encoder: dp->encoder.funcs->destroy(&dp->encoder); @@ -342,6 +402,8 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master, { struct rockchip_dp_device *dp = dev_get_drvdata(dev); + if (dp->audio_pdev) + platform_device_unregister(dp->audio_pdev); analogix_dp_unbind(dp->adp); dp->encoder.funcs->destroy(&dp->encoder); } @@ -446,6 +508,7 @@ static const struct rockchip_dp_chip_data rk3288_dp = { static const struct rockchip_dp_chip_data rk3568_edp = { .chip_type = RK3568_EDP, .ssc = true, + .audio = true, }; static const struct of_device_id rockchip_dp_dt_ids[] = { diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index b1fc00ee449e..2ffa52afcf43 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -8,6 +8,7 @@ #define _ANALOGIX_DP_H_ #include +#include struct analogix_dp_device; @@ -59,4 +60,12 @@ void analogix_dp_remove(struct analogix_dp_device *dp); int analogix_dp_start_crc(struct drm_connector *connector); int analogix_dp_stop_crc(struct drm_connector *connector); +int analogix_dp_audio_hw_params(struct analogix_dp_device *dp, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params); +void analogix_dp_audio_shutdown(struct analogix_dp_device *dp); +int analogix_dp_audio_startup(struct analogix_dp_device *dp); +int analogix_dp_audio_get_eld(struct analogix_dp_device *dp, + u8 *buf, size_t len); + #endif /* _ANALOGIX_DP_H_ */