lkml.org 
[lkml]   [2022]   [Sep]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH v1 12/17] drm/mediatek: hdmi: mt8195: add audio support
    Add HDMI audio support for mt8195

    Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>

    diff --git a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c
    index 39e07a6dd490..bb7593ea4c86 100644
    --- a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c
    +++ b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c
    @@ -215,6 +215,26 @@ static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black)
    mtk_hdmi_mask(hdmi, TOP_VMUTE_CFG1, 0, REG_VMUTE_EN);
    }

    +static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi)
    +{
    + u32 val;
    +
    + val = mtk_hdmi_read(hdmi, AIP_CTRL, &val);
    +
    + if (val & DSD_EN)
    + mtk_hdmi_mask(hdmi, AIP_TXCTRL,
    + DSD_MUTE_DATA | AUD_MUTE_FIFO_EN,
    + DSD_MUTE_DATA | AUD_MUTE_FIFO_EN);
    + else
    + mtk_hdmi_mask(hdmi, AIP_TXCTRL, AUD_MUTE_FIFO_EN,
    + AUD_MUTE_FIFO_EN);
    +}
    +
    +static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi)
    +{
    + mtk_hdmi_mask(hdmi, AIP_TXCTRL, AUD_MUTE_DIS, AUD_MUTE_FIFO_EN);
    +}
    +
    static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi)
    {
    mtk_hdmi_mask(hdmi, HDMITX_CONFIG, 0x0 << HDMITX_SW_RSTB_SHIFT,
    @@ -899,6 +919,7 @@ static void mtk_hdmi_audio_reset(struct mtk_hdmi *hdmi, bool rst)
    static void mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
    struct drm_display_mode *display_mode)
    {
    + mtk_hdmi_hw_aud_mute(hdmi);
    mtk_hdmi_aud_enable_packet(hdmi, false);
    mtk_hdmi_audio_reset(hdmi, true);
    mtk_hdmi_aip_ctrl_init(hdmi);
    @@ -911,6 +932,7 @@ static void mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
    usleep_range(25, 50);
    mtk_hdmi_aud_on_off_hw_ncts(hdmi, true);
    mtk_hdmi_aud_enable_packet(hdmi, true);
    + mtk_hdmi_hw_aud_unmute(hdmi);
    }

    void mtk_hdmi_output_init_mt8195(struct mtk_hdmi *hdmi)
    @@ -945,6 +967,28 @@ static void mtk_hdmi_reset_colorspace_setting(struct mtk_hdmi *hdmi)
    hdmi->ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
    }

    +static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi)
    +{
    + mtk_hdmi_aud_enable_packet(hdmi, true);
    + hdmi->audio_enable = true;
    +}
    +
    +static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi)
    +{
    + mtk_hdmi_aud_enable_packet(hdmi, false);
    + hdmi->audio_enable = false;
    +}
    +
    +static void mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi,
    + struct hdmi_audio_param *param)
    +{
    + if (!hdmi->audio_enable)
    + return;
    +
    + memcpy(&hdmi->aud_param, param, sizeof(*param));
    + mtk_hdmi_aud_output_config(hdmi, &hdmi->mode);
    +}
    +
    static void mtk_hdmi_change_video_resolution(struct mtk_hdmi *hdmi)
    {
    bool is_over_340M = false;
    @@ -965,6 +1009,7 @@ static void mtk_hdmi_change_video_resolution(struct mtk_hdmi *hdmi)

    usleep_range(5, 10);
    mtk_hdmi_hw_vid_black(hdmi, true);
    + mtk_hdmi_hw_aud_mute(hdmi);
    mtk_hdmi_hw_send_av_unmute(hdmi);

    mtk_hdmi_mask(hdmi, TOP_CFG01, NULL_PKT_VSYNC_HIGH_EN,
    @@ -1295,12 +1340,21 @@ static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge,
    mtk_hdmi_hw_send_av_mute(hdmi);
    usleep_range(50000, 50050);
    mtk_hdmi_hw_vid_black(hdmi, true);
    + mtk_hdmi_hw_aud_mute(hdmi);
    mtk_hdmi_disable_hdcp_encrypt(hdmi);
    usleep_range(50000, 50050);

    hdmi->enabled = false;
    }

    +static void mtk_hdmi_handle_plugged_change(struct mtk_hdmi *hdmi, bool plugged)
    +{
    + mutex_lock(&hdmi->update_plugged_status_lock);
    + if (hdmi->plugged_cb && hdmi->codec_dev)
    + hdmi->plugged_cb(hdmi->codec_dev, plugged);
    + mutex_unlock(&hdmi->update_plugged_status_lock);
    +}
    +
    static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge,
    struct drm_bridge_state *old_state)
    {
    @@ -1314,6 +1368,9 @@ static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge,
    hdmi->powered = false;

    mtk_hdmi_reset_colorspace_setting(hdmi);
    +
    + /* signal the disconnect event to audio codec */
    + mtk_hdmi_handle_plugged_change(hdmi, false);
    }

    static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge,
    @@ -1346,6 +1403,10 @@ static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge,
    mtk_hdmi_hw_avi_infoframe(hdmi, buffer_avi, sizeof(buffer_avi));

    mtk_hdmi_hw_vid_black(hdmi, false);
    + mtk_hdmi_hw_aud_unmute(hdmi);
    +
    + /* signal the connect event to audio codec */
    + mtk_hdmi_handle_plugged_change(hdmi, true);

    hdmi->enabled = true;
    }
    @@ -1385,3 +1446,155 @@ const struct drm_bridge_funcs mtk_mt8195_hdmi_bridge_funcs = {
    .get_edid = mtk_hdmi_bridge_get_edid,
    .detect = mtk_hdmi_bridge_detect,
    };
    +
    +static void mtk_hdmi_set_plugged_cb(struct mtk_hdmi *hdmi,
    + hdmi_codec_plugged_cb fn,
    + struct device *codec_dev)
    +{
    + bool plugged;
    +
    + mutex_lock(&hdmi->update_plugged_status_lock);
    + hdmi->plugged_cb = fn;
    + hdmi->codec_dev = codec_dev;
    + plugged = (hdmi->hpd == HDMI_PLUG_IN_AND_SINK_POWER_ON) ? true : false;
    + mutex_unlock(&hdmi->update_plugged_status_lock);
    +
    + mtk_hdmi_handle_plugged_change(hdmi, plugged);
    +}
    +
    +/*
    + * HDMI audio codec callbacks
    + */
    +static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data,
    + hdmi_codec_plugged_cb fn,
    + struct device *codec_dev)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    +
    + if (!hdmi)
    + return -ENODEV;
    +
    + mtk_hdmi_set_plugged_cb(hdmi, fn, codec_dev);
    + return 0;
    +}
    +
    +static int mtk_hdmi_audio_hw_params(struct device *dev, void *data,
    + struct hdmi_codec_daifmt *daifmt,
    + struct hdmi_codec_params *params)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    + struct hdmi_audio_param hdmi_params;
    + unsigned int chan = params->cea.channels;
    +
    + if (!hdmi->bridge.encoder)
    + return -ENODEV;
    +
    + switch (chan) {
    + case 2:
    + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
    + break;
    + case 4:
    + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
    + break;
    + case 6:
    + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
    + break;
    + case 8:
    + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + switch (params->sample_rate) {
    + case 32000:
    + case 44100:
    + case 48000:
    + case 88200:
    + case 96000:
    + case 176400:
    + case 192000:
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + switch (daifmt->fmt) {
    + case HDMI_I2S:
    + hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
    + hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
    + hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S;
    + hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
    + hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + memcpy(&hdmi_params.codec_params, params,
    + sizeof(hdmi_params.codec_params));
    +
    + mtk_hdmi_audio_set_param(hdmi, &hdmi_params);
    +
    + return 0;
    +}
    +
    +static int mtk_hdmi_audio_startup(struct device *dev, void *data)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    +
    + mtk_hdmi_audio_enable(hdmi);
    +
    + return 0;
    +}
    +
    +static void mtk_hdmi_audio_shutdown(struct device *dev, void *data)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    +
    + mtk_hdmi_audio_disable(hdmi);
    +}
    +
    +static int mtk_hdmi_audio_mute(struct device *dev, void *data, bool enable,
    + int direction)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    +
    + if (direction != SNDRV_PCM_STREAM_PLAYBACK)
    + return 0;
    +
    + if (enable)
    + mtk_hdmi_hw_aud_mute(hdmi);
    + else
    + mtk_hdmi_hw_aud_unmute(hdmi);
    +
    + return 0;
    +}
    +
    +static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
    + size_t len)
    +{
    + struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
    +
    + if (hdmi->enabled)
    + memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len));
    + else
    + memset(buf, 0, len);
    + return 0;
    +}
    +
    +static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = {
    + .hw_params = mtk_hdmi_audio_hw_params,
    + .audio_startup = mtk_hdmi_audio_startup,
    + .audio_shutdown = mtk_hdmi_audio_shutdown,
    + .mute_stream = mtk_hdmi_audio_mute,
    + .get_eld = mtk_hdmi_audio_get_eld,
    + .hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb,
    +};
    +
    +void set_hdmi_codec_pdata_mt8195(struct hdmi_codec_pdata *codec_data)
    +{
    + codec_data->ops = &mtk_hdmi_audio_codec_ops;
    + codec_data->max_i2s_channels = 2;
    + codec_data->i2s = 1;
    +}
    diff --git a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h
    index f59aea51dc74..22af64916219 100644
    --- a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h
    +++ b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.h
    @@ -9,6 +9,7 @@

    #include <linux/hdmi.h>
    #include <drm/drm_bridge.h>
    +#include <sound/hdmi-codec.h>

    struct mtk_hdmi;

    @@ -17,6 +18,7 @@ extern const struct drm_bridge_funcs mtk_mt8195_hdmi_bridge_funcs;
    void mtk_hdmi_output_init_mt8195(struct mtk_hdmi *hdmi);
    int mtk_hdmi_clk_enable_mt8195(struct mtk_hdmi *hdmi);
    void mtk_hdmi_clk_disable_mt8195(struct mtk_hdmi *hdmi);
    +void set_hdmi_codec_pdata_mt8195(struct hdmi_codec_pdata *codec_data);

    enum mtk_hdmi_clk_id_mt8195 {
    MTK_MT8195_HDIM_HDCP_SEL,
    --
    b4 0.10.0-dev

    \
     
     \ /
      Last update: 2022-09-19 19:02    [W:4.447 / U:0.248 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site