| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * MediaTek HDMI v2 IP driver
- *
- * Copyright (c) 2022 MediaTek Inc.
- * Copyright (c) 2022 BayLibre, SAS
- * Copyright (c) 2024 Collabora Ltd.
- * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
- */
- #include <linux/bitfield.h>
- #include <linux/clk.h>
- #include <linux/debugfs.h>
- #include <linux/delay.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/kernel.h>
- #include <linux/mutex.h>
- #include <linux/of.h>
- #include <linux/of_platform.h>
- #include <linux/pm_runtime.h>
- #include <linux/regmap.h>
- #include <linux/suspend.h>
- #include <linux/units.h>
- #include <linux/phy/phy.h>
- #include <drm/display/drm_hdmi_helper.h>
- #include <drm/display/drm_hdmi_state_helper.h>
- #include <drm/display/drm_scdc_helper.h>
- #include <drm/drm_drv.h>
- #include <drm/drm_edid.h>
- #include <drm/drm_print.h>
- #include <drm/drm_probe_helper.h>
- #include "mtk_hdmi_common.h"
- #include "mtk_hdmi_regs_v2.h"
- #define MTK_HDMI_V2_CLOCK_MIN 27000
- #define MTK_HDMI_V2_CLOCK_MAX 594000
- #define HPD_PORD_HWIRQS (HTPLG_R_INT | HTPLG_F_INT | PORD_F_INT | PORD_R_INT)
- enum mtk_hdmi_v2_clk_id {
- MTK_HDMI_V2_CLK_HDCP_SEL,
- MTK_HDMI_V2_CLK_HDCP_24M_SEL,
- MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI,
- MTK_HDMI_V2_CLK_HDMI_APB_SEL,
- MTK_HDMI_V2_CLK_COUNT,
- };
- const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = {
- [MTK_HDMI_V2_CLK_HDMI_APB_SEL] = "bus",
- [MTK_HDMI_V2_CLK_HDCP_SEL] = "hdcp",
- [MTK_HDMI_V2_CLK_HDCP_24M_SEL] = "hdcp24m",
- [MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI] = "hdmi-split",
- };
- static inline void mtk_hdmi_v2_hwirq_disable(struct mtk_hdmi *hdmi)
- {
- regmap_write(hdmi->regs, TOP_INT_ENABLE00, 0);
- regmap_write(hdmi->regs, TOP_INT_ENABLE01, 0);
- }
- static inline void mtk_hdmi_v2_enable_hpd_pord_irq(struct mtk_hdmi *hdmi, bool enable)
- {
- if (enable)
- regmap_set_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
- else
- regmap_clear_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
- }
- static inline void mtk_hdmi_v2_set_sw_hpd(struct mtk_hdmi *hdmi, bool enable)
- {
- if (enable) {
- regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
- regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
- regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
- } else {
- regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
- regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
- regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
- }
- }
- static inline void mtk_hdmi_v2_enable_scrambling(struct mtk_hdmi *hdmi, bool enable)
- {
- struct drm_scdc *scdc = &hdmi->curr_conn->display_info.hdmi.scdc;
- if (enable)
- regmap_set_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
- else
- regmap_clear_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
- if (scdc->supported) {
- if (scdc->scrambling.supported)
- drm_scdc_set_scrambling(hdmi->curr_conn, enable);
- drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, enable);
- }
- }
- static void mtk_hdmi_v2_hw_vid_mute(struct mtk_hdmi *hdmi, bool enable)
- {
- /* If enabled, sends a black image */
- if (enable)
- regmap_set_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
- else
- regmap_clear_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
- }
- static void mtk_hdmi_v2_hw_aud_mute(struct mtk_hdmi *hdmi, bool enable)
- {
- u32 aip, val;
- if (!enable) {
- regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_MUTE_FIFO_EN);
- return;
- }
- regmap_read(hdmi->regs, AIP_CTRL, &aip);
- val = AUD_MUTE_FIFO_EN;
- if (aip & DSD_EN)
- val |= DSD_MUTE_EN;
- regmap_update_bits(hdmi->regs, AIP_TXCTRL, val, val);
- }
- static void mtk_hdmi_v2_hw_reset(struct mtk_hdmi *hdmi)
- {
- regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
- udelay(5);
- regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
- }
- static inline u32 mtk_hdmi_v2_format_hw_packet(const u8 *buffer, u8 len)
- {
- unsigned short i;
- u32 val = 0;
- for (i = 0; i < len; i++)
- val |= buffer[i] << (i * 8);
- return val;
- }
- static int mtk_hdmi_v2_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
- const u8 *buffer, size_t len)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
- regmap_write(hdmi->regs, TOP_AIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
- regmap_write(hdmi->regs, TOP_AIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 3));
- regmap_write(hdmi->regs, TOP_AIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
- regmap_write(hdmi->regs, TOP_AIF_PKT02, 0);
- regmap_write(hdmi->regs, TOP_AIF_PKT03, 0);
- regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
- regmap_set_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
- const u8 *buffer, size_t len)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
- regmap_write(hdmi->regs, TOP_AVI_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
- regmap_write(hdmi->regs, TOP_AVI_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
- regmap_write(hdmi->regs, TOP_AVI_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
- regmap_write(hdmi->regs, TOP_AVI_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
- regmap_write(hdmi->regs, TOP_AVI_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
- regmap_write(hdmi->regs, TOP_AVI_PKT04, 0);
- regmap_write(hdmi->regs, TOP_AVI_PKT05, 0);
- regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
- regmap_set_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
- const u8 *buffer, size_t len)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
- regmap_write(hdmi->regs, TOP_SPDIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT04, mtk_hdmi_v2_format_hw_packet(&buffer[17], 4));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT05, mtk_hdmi_v2_format_hw_packet(&buffer[21], 3));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT06, mtk_hdmi_v2_format_hw_packet(&buffer[24], 4));
- regmap_write(hdmi->regs, TOP_SPDIF_PKT07, buffer[28]);
- regmap_set_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
- regmap_set_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
- const u8 *buffer, size_t len)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
- regmap_write(hdmi->regs, TOP_VSIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
- regmap_write(hdmi->regs, TOP_VSIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
- regmap_write(hdmi->regs, TOP_VSIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
- regmap_write(hdmi->regs, TOP_VSIF_PKT02, 0);
- regmap_write(hdmi->regs, TOP_VSIF_PKT03, 0);
- regmap_write(hdmi->regs, TOP_VSIF_PKT04, 0);
- regmap_write(hdmi->regs, TOP_VSIF_PKT05, 0);
- regmap_write(hdmi->regs, TOP_VSIF_PKT06, 0);
- regmap_write(hdmi->regs, TOP_VSIF_PKT07, 0);
- regmap_set_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
- regmap_set_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
- return 0;
- }
- static void mtk_hdmi_yuv420_downsampling(struct mtk_hdmi *hdmi, bool enable)
- {
- u32 val;
- regmap_read(hdmi->regs, VID_DOWNSAMPLE_CONFIG, &val);
- if (enable) {
- regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMI_YUV420_MODE);
- val |= C444_C422_CONFIG_ENABLE | C422_C420_CONFIG_ENABLE;
- val |= C422_C420_CONFIG_OUT_CB_OR_CR;
- val &= ~C422_C420_CONFIG_BYPASS;
- regmap_write(hdmi->regs, VID_DOWNSAMPLE_CONFIG, val);
- regmap_set_bits(hdmi->regs, VID_OUT_FORMAT, OUTPUT_FORMAT_DEMUX_420_ENABLE);
- } else {
- regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMI_YUV420_MODE);
- val &= ~(C444_C422_CONFIG_ENABLE | C422_C420_CONFIG_ENABLE);
- val &= ~C422_C420_CONFIG_OUT_CB_OR_CR;
- val |= C422_C420_CONFIG_BYPASS;
- regmap_write(hdmi->regs, VID_DOWNSAMPLE_CONFIG, val);
- regmap_clear_bits(hdmi->regs, VID_OUT_FORMAT, OUTPUT_FORMAT_DEMUX_420_ENABLE);
- }
- }
- static int mtk_hdmi_v2_setup_audio_infoframe(struct mtk_hdmi *hdmi)
- {
- struct hdmi_codec_params *params = &hdmi->aud_param.codec_params;
- struct hdmi_audio_infoframe frame;
- u8 buffer[14];
- ssize_t ret;
- memcpy(&frame, ¶ms->cea, sizeof(frame));
- ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
- if (ret < 0)
- return ret;
- mtk_hdmi_v2_hdmi_write_audio_infoframe(&hdmi->bridge, buffer, sizeof(buffer));
- return 0;
- }
- static inline void mtk_hdmi_v2_hw_gcp_avmute(struct mtk_hdmi *hdmi, bool mute)
- {
- u32 val;
- regmap_read(hdmi->regs, TOP_CFG01, &val);
- val &= ~(CP_CLR_MUTE_EN | CP_SET_MUTE_EN);
- if (mute) {
- val |= CP_SET_MUTE_EN;
- val &= ~CP_CLR_MUTE_EN;
- } else {
- val |= CP_CLR_MUTE_EN;
- val &= ~CP_SET_MUTE_EN;
- }
- regmap_write(hdmi->regs, TOP_CFG01, val);
- regmap_set_bits(hdmi->regs, TOP_INFO_RPT, CP_RPT_EN);
- regmap_set_bits(hdmi->regs, TOP_INFO_EN, CP_EN | CP_EN_WR);
- }
- static void mtk_hdmi_v2_hw_ncts_enable(struct mtk_hdmi *hdmi, bool enable)
- {
- if (enable)
- regmap_set_bits(hdmi->regs, AIP_CTRL, CTS_SW_SEL);
- else
- regmap_clear_bits(hdmi->regs, AIP_CTRL, CTS_SW_SEL);
- }
- static void mtk_hdmi_v2_hw_aud_set_channel_status(struct mtk_hdmi *hdmi)
- {
- u8 *ch_status = hdmi->aud_param.codec_params.iec.status;
- /* Only the first 5 to 7 bytes of Channel Status contain useful information */
- regmap_write(hdmi->regs, AIP_I2S_CHST0, mtk_hdmi_v2_format_hw_packet(&ch_status[0], 4));
- regmap_write(hdmi->regs, AIP_I2S_CHST1, mtk_hdmi_v2_format_hw_packet(&ch_status[4], 3));
- }
- static void mtk_hdmi_v2_hw_aud_set_ncts(struct mtk_hdmi *hdmi,
- unsigned int sample_rate,
- unsigned int clock)
- {
- unsigned int n, cts;
- mtk_hdmi_get_ncts(sample_rate, clock, &n, &cts);
- regmap_write(hdmi->regs, AIP_N_VAL, n);
- regmap_write(hdmi->regs, AIP_CTS_SVAL, cts);
- }
- static void mtk_hdmi_v2_hw_aud_enable(struct mtk_hdmi *hdmi, bool enable)
- {
- if (enable)
- regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_PACKET_DROP);
- else
- regmap_set_bits(hdmi->regs, AIP_TXCTRL, AUD_PACKET_DROP);
- }
- static u32 mtk_hdmi_v2_aud_output_channel_map(u8 sd0, u8 sd1, u8 sd2, u8 sd3,
- u8 sd4, u8 sd5, u8 sd6, u8 sd7)
- {
- u32 val;
- /*
- * Each of the Output Channels (0-7) can be mapped to get their input
- * from any of the available Input Channels (0-7): this function
- * takes input channel numbers and formats a value that must then
- * be written to the TOP_AUD_MAP hardware register by the caller.
- */
- val = FIELD_PREP(SD0_MAP, sd0) | FIELD_PREP(SD1_MAP, sd1);
- val |= FIELD_PREP(SD2_MAP, sd2) | FIELD_PREP(SD3_MAP, sd3);
- val |= FIELD_PREP(SD4_MAP, sd4) | FIELD_PREP(SD5_MAP, sd5);
- val |= FIELD_PREP(SD6_MAP, sd6) | FIELD_PREP(SD7_MAP, sd7);
- return val;
- }
- static void mtk_hdmi_audio_dsd_config(struct mtk_hdmi *hdmi,
- unsigned char chnum, bool dsd_bypass)
- {
- u32 channel_map;
- regmap_update_bits(hdmi->regs, AIP_CTRL, SPDIF_EN | DSD_EN | HBRA_ON, DSD_EN);
- regmap_set_bits(hdmi->regs, AIP_TXCTRL, DSD_MUTE_EN);
- if (dsd_bypass)
- channel_map = mtk_hdmi_v2_aud_output_channel_map(0, 2, 4, 6, 1, 3, 5, 7);
- else
- channel_map = mtk_hdmi_v2_aud_output_channel_map(0, 5, 1, 0, 3, 2, 4, 0);
- regmap_write(hdmi->regs, TOP_AUD_MAP, channel_map);
- regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, I2S2DSD_EN);
- }
- static inline void mtk_hdmi_v2_hw_i2s_fifo_map(struct mtk_hdmi *hdmi, u32 fifo_mapping)
- {
- regmap_update_bits(hdmi->regs, AIP_I2S_CTRL,
- FIFO0_MAP | FIFO1_MAP | FIFO2_MAP | FIFO3_MAP, fifo_mapping);
- }
- static inline void mtk_hdmi_v2_hw_i2s_ch_number(struct mtk_hdmi *hdmi, u8 chnum)
- {
- regmap_update_bits(hdmi->regs, AIP_CTRL, I2S_EN, FIELD_PREP(I2S_EN, chnum));
- }
- static void mtk_hdmi_v2_hw_i2s_ch_mapping(struct mtk_hdmi *hdmi, u8 chnum, u8 mapping)
- {
- u32 fifo_map;
- u8 bdata;
- switch (chnum) {
- default:
- case 2:
- bdata = 0x1;
- break;
- case 3:
- bdata = 0x3;
- break;
- case 6:
- if (mapping == 0x0e) {
- bdata = 0xf;
- break;
- }
- fallthrough;
- case 5:
- bdata = 0x7;
- break;
- case 7:
- case 8:
- bdata = 0xf;
- break;
- }
- /* Assign default FIFO mapping: SD0 to FIFO0, SD1 to FIFO1, etc. */
- fifo_map = FIELD_PREP(FIFO0_MAP, 0) | FIELD_PREP(FIFO1_MAP, 1);
- fifo_map |= FIELD_PREP(FIFO2_MAP, 2) | FIELD_PREP(FIFO3_MAP, 3);
- mtk_hdmi_v2_hw_i2s_fifo_map(hdmi, fifo_map);
- mtk_hdmi_v2_hw_i2s_ch_number(hdmi, bdata);
- /*
- * Set HDMI Audio packet layout indicator:
- * Layout 0 is for two channels
- * Layout 1 is for up to eight channels
- */
- if (chnum == 2)
- regmap_set_bits(hdmi->regs, AIP_TXCTRL, AUD_LAYOUT_1);
- else
- regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_LAYOUT_1);
- }
- static void mtk_hdmi_i2s_data_fmt(struct mtk_hdmi *hdmi, unsigned char fmt)
- {
- u32 val;
- regmap_read(hdmi->regs, AIP_I2S_CTRL, &val);
- val &= ~(WS_HIGH | I2S_1ST_BIT_NOSHIFT | JUSTIFY_RIGHT);
- switch (fmt) {
- case HDMI_I2S_MODE_RJT_24BIT:
- case HDMI_I2S_MODE_RJT_16BIT:
- val |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT | JUSTIFY_RIGHT);
- break;
- case HDMI_I2S_MODE_LJT_24BIT:
- case HDMI_I2S_MODE_LJT_16BIT:
- val |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT);
- break;
- case HDMI_I2S_MODE_I2S_24BIT:
- case HDMI_I2S_MODE_I2S_16BIT:
- default:
- break;
- }
- regmap_write(hdmi->regs, AIP_I2S_CTRL, val);
- }
- static inline void mtk_hdmi_i2s_sck_edge_rise(struct mtk_hdmi *hdmi, bool rise)
- {
- if (rise)
- regmap_set_bits(hdmi->regs, AIP_I2S_CTRL, SCK_EDGE_RISE);
- else
- regmap_clear_bits(hdmi->regs, AIP_I2S_CTRL, SCK_EDGE_RISE);
- }
- static inline void mtk_hdmi_i2s_cbit_order(struct mtk_hdmi *hdmi, unsigned int cbit)
- {
- regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, CBIT_ORDER_SAME, cbit);
- }
- static inline void mtk_hdmi_i2s_vbit(struct mtk_hdmi *hdmi, unsigned int vbit)
- {
- /* V bit: 0 for PCM, 1 for Compressed data */
- regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, VBIT_COMPRESSED, vbit);
- }
- static inline void mtk_hdmi_i2s_data_direction(struct mtk_hdmi *hdmi, unsigned int is_lsb)
- {
- regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, I2S_DATA_DIR_LSB, is_lsb);
- }
- static inline void mtk_hdmi_v2_hw_audio_type(struct mtk_hdmi *hdmi, unsigned int spdif_i2s)
- {
- regmap_update_bits(hdmi->regs, AIP_CTRL, SPDIF_EN, FIELD_PREP(SPDIF_EN, spdif_i2s));
- }
- static u8 mtk_hdmi_v2_get_i2s_ch_mapping(struct mtk_hdmi *hdmi, u8 channel_type)
- {
- switch (channel_type) {
- case HDMI_AUD_CHAN_TYPE_1_1:
- case HDMI_AUD_CHAN_TYPE_2_1:
- return 0x01;
- case HDMI_AUD_CHAN_TYPE_3_0:
- return 0x02;
- case HDMI_AUD_CHAN_TYPE_3_1:
- return 0x03;
- case HDMI_AUD_CHAN_TYPE_3_0_LRS:
- case HDMI_AUD_CHAN_TYPE_4_0:
- return 0x08;
- case HDMI_AUD_CHAN_TYPE_5_1:
- return 0x0b;
- case HDMI_AUD_CHAN_TYPE_4_1_CLRS:
- case HDMI_AUD_CHAN_TYPE_6_0:
- case HDMI_AUD_CHAN_TYPE_6_0_CS:
- case HDMI_AUD_CHAN_TYPE_6_0_CH:
- case HDMI_AUD_CHAN_TYPE_6_0_OH:
- case HDMI_AUD_CHAN_TYPE_6_0_CHR:
- return 0x0e;
- case HDMI_AUD_CHAN_TYPE_1_0:
- case HDMI_AUD_CHAN_TYPE_2_0:
- case HDMI_AUD_CHAN_TYPE_3_1_LRS:
- case HDMI_AUD_CHAN_TYPE_4_1:
- case HDMI_AUD_CHAN_TYPE_5_0:
- case HDMI_AUD_CHAN_TYPE_4_0_CLRS:
- case HDMI_AUD_CHAN_TYPE_6_1:
- case HDMI_AUD_CHAN_TYPE_6_1_CS:
- case HDMI_AUD_CHAN_TYPE_6_1_CH:
- case HDMI_AUD_CHAN_TYPE_6_1_OH:
- case HDMI_AUD_CHAN_TYPE_6_1_CHR:
- case HDMI_AUD_CHAN_TYPE_7_0:
- case HDMI_AUD_CHAN_TYPE_7_0_LH_RH:
- case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR:
- case HDMI_AUD_CHAN_TYPE_7_0_LC_RC:
- case HDMI_AUD_CHAN_TYPE_7_0_LW_RW:
- case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD:
- case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS:
- case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS:
- case HDMI_AUD_CHAN_TYPE_7_0_CS_CH:
- case HDMI_AUD_CHAN_TYPE_7_0_CS_OH:
- case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR:
- case HDMI_AUD_CHAN_TYPE_7_0_CH_OH:
- case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR:
- case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR:
- case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR:
- case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS:
- case HDMI_AUD_CHAN_TYPE_7_1:
- case HDMI_AUD_CHAN_TYPE_7_1_LH_RH:
- case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR:
- case HDMI_AUD_CHAN_TYPE_7_1_LC_RC:
- case HDMI_AUD_CHAN_TYPE_7_1_LW_RW:
- case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD:
- case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS:
- case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS:
- case HDMI_AUD_CHAN_TYPE_7_1_CS_CH:
- case HDMI_AUD_CHAN_TYPE_7_1_CS_OH:
- case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR:
- case HDMI_AUD_CHAN_TYPE_7_1_CH_OH:
- case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR:
- case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR:
- case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR:
- default:
- return 0;
- }
- return 0;
- }
- static inline void mtk_hdmi_v2_hw_i2s_ch_swap(struct mtk_hdmi *hdmi)
- {
- regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_I2S_HI_WRITE,
- FIELD_PREP(MAX_2UI_I2S_HI_WRITE, MAX_2UI_I2S_LFE_CC_SWAP));
- }
- static void mtk_hdmi_hbr_config(struct mtk_hdmi *hdmi, bool dsd_bypass)
- {
- const u32 hbr_mask = SPDIF_EN | DSD_EN | HBRA_ON;
- if (dsd_bypass) {
- regmap_update_bits(hdmi->regs, AIP_CTRL, hbr_mask, HBRA_ON);
- regmap_set_bits(hdmi->regs, AIP_CTRL, I2S_EN);
- } else {
- regmap_update_bits(hdmi->regs, AIP_CTRL, hbr_mask, SPDIF_EN);
- regmap_set_bits(hdmi->regs, AIP_CTRL, SPDIF_INTERNAL_MODULE);
- regmap_set_bits(hdmi->regs, AIP_CTRL, HBR_FROM_SPDIF);
- regmap_set_bits(hdmi->regs, AIP_CTRL, CTS_CAL_N4);
- }
- }
- static inline void mtk_hdmi_v2_hw_spdif_config(struct mtk_hdmi *hdmi)
- {
- regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, WR_1UI_LOCK);
- regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, FS_OVERRIDE_WRITE);
- regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, WR_2UI_LOCK);
- regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_1UI_WRITE,
- FIELD_PREP(MAX_1UI_WRITE, 4));
- regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_SPDIF_WRITE,
- FIELD_PREP(MAX_2UI_SPDIF_WRITE, 9));
- regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, AUD_ERR_THRESH,
- FIELD_PREP(AUD_ERR_THRESH, 4));
- regmap_set_bits(hdmi->regs, AIP_SPDIF_CTRL, I2S2DSD_EN);
- }
- static void mtk_hdmi_v2_aud_set_input(struct mtk_hdmi *hdmi)
- {
- struct hdmi_audio_param *aud_param = &hdmi->aud_param;
- struct hdmi_codec_params *codec_params = &aud_param->codec_params;
- u8 i2s_ch_map;
- u32 out_ch_map;
- /* Write the default output channel map. CH0 maps to SD0, CH1 maps to SD1, etc */
- out_ch_map = mtk_hdmi_v2_aud_output_channel_map(0, 1, 2, 3, 4, 5, 6, 7);
- regmap_write(hdmi->regs, TOP_AUD_MAP, out_ch_map);
- regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_I2S_HI_WRITE, 0);
- regmap_clear_bits(hdmi->regs, AIP_CTRL,
- SPDIF_EN | DSD_EN | HBRA_ON | CTS_CAL_N4 |
- HBR_FROM_SPDIF | SPDIF_INTERNAL_MODULE);
- regmap_clear_bits(hdmi->regs, AIP_TXCTRL, DSD_MUTE_EN | AUD_LAYOUT_1);
- if (aud_param->aud_input_type == HDMI_AUD_INPUT_I2S) {
- switch (aud_param->aud_codec) {
- case HDMI_AUDIO_CODING_TYPE_DTS_HD:
- case HDMI_AUDIO_CODING_TYPE_MLP:
- mtk_hdmi_i2s_data_fmt(hdmi, aud_param->aud_i2s_fmt);
- mtk_hdmi_hbr_config(hdmi, true);
- break;
- case HDMI_AUDIO_CODING_TYPE_DSD:
- mtk_hdmi_audio_dsd_config(hdmi, codec_params->channels, 0);
- mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, codec_params->channels, 1);
- break;
- default:
- mtk_hdmi_i2s_data_fmt(hdmi, aud_param->aud_i2s_fmt);
- mtk_hdmi_i2s_sck_edge_rise(hdmi, true);
- mtk_hdmi_i2s_cbit_order(hdmi, CBIT_ORDER_SAME);
- mtk_hdmi_i2s_vbit(hdmi, 0); /* PCM data */
- mtk_hdmi_i2s_data_direction(hdmi, 0); /* MSB first */
- mtk_hdmi_v2_hw_audio_type(hdmi, HDMI_AUD_INPUT_I2S);
- i2s_ch_map = mtk_hdmi_v2_get_i2s_ch_mapping(hdmi,
- aud_param->aud_input_chan_type);
- mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, codec_params->channels, i2s_ch_map);
- mtk_hdmi_v2_hw_i2s_ch_swap(hdmi);
- }
- } else {
- if (codec_params->sample_rate == 768000 &&
- (aud_param->aud_codec == HDMI_AUDIO_CODING_TYPE_DTS_HD ||
- aud_param->aud_codec == HDMI_AUDIO_CODING_TYPE_MLP)) {
- mtk_hdmi_hbr_config(hdmi, false);
- } else {
- mtk_hdmi_v2_hw_spdif_config(hdmi);
- mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, 2, 0);
- }
- }
- }
- static inline void mtk_hdmi_v2_hw_audio_input_enable(struct mtk_hdmi *hdmi, bool ena)
- {
- if (ena)
- regmap_set_bits(hdmi->regs, AIP_CTRL, AUD_IN_EN);
- else
- regmap_clear_bits(hdmi->regs, AIP_CTRL, AUD_IN_EN);
- }
- static void mtk_hdmi_v2_aip_ctrl_init(struct mtk_hdmi *hdmi)
- {
- regmap_set_bits(hdmi->regs, AIP_CTRL,
- AUD_SEL_OWRT | NO_MCLK_CTSGEN_SEL | MCLK_EN | CTS_REQ_EN);
- regmap_clear_bits(hdmi->regs, AIP_TPI_CTRL, TPI_AUDIO_LOOKUP_EN);
- }
- static void mtk_hdmi_v2_audio_reset(struct mtk_hdmi *hdmi, bool reset)
- {
- const u32 arst_bits = RST4AUDIO | RST4AUDIO_FIFO | RST4AUDIO_ACR;
- if (reset)
- regmap_set_bits(hdmi->regs, AIP_TXCTRL, arst_bits);
- else
- regmap_clear_bits(hdmi->regs, AIP_TXCTRL, arst_bits);
- }
- static void mtk_hdmi_v2_aud_output_config(struct mtk_hdmi *hdmi,
- struct drm_display_mode *display_mode)
- {
- /* Shut down and reset the HDMI Audio HW to avoid glitching */
- mtk_hdmi_v2_hw_aud_mute(hdmi, true);
- mtk_hdmi_v2_hw_aud_enable(hdmi, false);
- mtk_hdmi_v2_audio_reset(hdmi, true);
- /* Configure the main hardware params and get out of reset */
- mtk_hdmi_v2_aip_ctrl_init(hdmi);
- mtk_hdmi_v2_aud_set_input(hdmi);
- mtk_hdmi_v2_hw_aud_set_channel_status(hdmi);
- mtk_hdmi_v2_setup_audio_infoframe(hdmi);
- mtk_hdmi_v2_hw_audio_input_enable(hdmi, true);
- mtk_hdmi_v2_audio_reset(hdmi, false);
- /* Ignore N/CTS packet transmission requests and configure */
- mtk_hdmi_v2_hw_ncts_enable(hdmi, false);
- mtk_hdmi_v2_hw_aud_set_ncts(hdmi, hdmi->aud_param.codec_params.sample_rate,
- display_mode->clock);
- /* Wait for the HW to apply settings */
- usleep_range(25, 50);
- /* Hardware is fully configured: enable TX of N/CTS pkts and unmute */
- mtk_hdmi_v2_hw_ncts_enable(hdmi, true);
- mtk_hdmi_v2_hw_aud_enable(hdmi, true);
- mtk_hdmi_v2_hw_aud_mute(hdmi, false);
- }
- static void mtk_hdmi_v2_change_video_resolution(struct mtk_hdmi *hdmi,
- struct drm_connector_state *conn_state)
- {
- mtk_hdmi_v2_hw_reset(hdmi);
- mtk_hdmi_v2_set_sw_hpd(hdmi, true);
- udelay(2);
- regmap_write(hdmi->regs, HDCP_TOP_CTRL, 0);
- /*
- * Enable HDCP reauthentication interrupt: the HW uses this internally
- * for the HPD state machine even if HDCP encryption is not enabled.
- */
- regmap_set_bits(hdmi->regs, TOP_INT_ENABLE00, HDCP2X_RX_REAUTH_REQ_DDCM_INT);
- /* Enable hotplug and pord interrupts */
- mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
- /* Force enabling HDCP HPD */
- regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
- regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
- /* Set 8 bits per pixel */
- regmap_update_bits(hdmi->regs, TOP_CFG00, TMDS_PACK_MODE,
- FIELD_PREP(TMDS_PACK_MODE, TMDS_PACK_MODE_8BPP));
- /* Disable generating deepcolor packets */
- regmap_clear_bits(hdmi->regs, TOP_CFG00, DEEPCOLOR_PKT_EN);
- /* Disable adding deepcolor information to the general packet */
- regmap_clear_bits(hdmi->regs, TOP_MISC_CTLR, DEEP_COLOR_ADD);
- if (hdmi->curr_conn->display_info.is_hdmi)
- regmap_set_bits(hdmi->regs, TOP_CFG00, HDMI_MODE_HDMI);
- else
- regmap_clear_bits(hdmi->regs, TOP_CFG00, HDMI_MODE_HDMI);
- udelay(5);
- mtk_hdmi_v2_hw_vid_mute(hdmi, true);
- mtk_hdmi_v2_hw_aud_mute(hdmi, true);
- mtk_hdmi_v2_hw_gcp_avmute(hdmi, false);
- regmap_update_bits(hdmi->regs, TOP_CFG01,
- NULL_PKT_VSYNC_HIGH_EN | NULL_PKT_EN, NULL_PKT_VSYNC_HIGH_EN);
- usleep_range(100, 150);
- /* Enable scrambling if tmds clock is 340MHz or more */
- mtk_hdmi_v2_enable_scrambling(hdmi, hdmi->mode.clock >= 340 * KILO);
- switch (conn_state->hdmi.output_format) {
- default:
- case HDMI_COLORSPACE_RGB:
- case HDMI_COLORSPACE_YUV444:
- /* Disable YUV420 downsampling for RGB and YUV444 */
- mtk_hdmi_yuv420_downsampling(hdmi, false);
- break;
- case HDMI_COLORSPACE_YUV422:
- /*
- * YUV420 downsampling is special and needs a bit of setup
- * so we disable everything there before doing anything else.
- *
- * YUV422 downsampling instead just needs one bit to be set.
- */
- mtk_hdmi_yuv420_downsampling(hdmi, false);
- regmap_set_bits(hdmi->regs, VID_DOWNSAMPLE_CONFIG,
- C444_C422_CONFIG_ENABLE);
- break;
- case HDMI_COLORSPACE_YUV420:
- mtk_hdmi_yuv420_downsampling(hdmi, true);
- break;
- }
- }
- static void mtk_hdmi_v2_output_set_display_mode(struct mtk_hdmi *hdmi,
- struct drm_connector_state *conn_state,
- struct drm_display_mode *mode)
- {
- union phy_configure_opts opts = {
- .dp = { .link_rate = hdmi->mode.clock * KILO }
- };
- int ret;
- ret = phy_configure(hdmi->phy, &opts);
- if (ret)
- dev_err(hdmi->dev, "Setting clock=%d failed: %d", mode->clock, ret);
- mtk_hdmi_v2_change_video_resolution(hdmi, conn_state);
- mtk_hdmi_v2_aud_output_config(hdmi, mode);
- }
- static int mtk_hdmi_v2_clk_enable(struct mtk_hdmi *hdmi)
- {
- int ret;
- ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
- if (ret)
- return ret;
- ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
- if (ret)
- goto disable_hdcp_clk;
- ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
- if (ret)
- goto disable_hdcp_24m_clk;
- ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI]);
- if (ret)
- goto disable_bus_clk;
- return 0;
- disable_bus_clk:
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
- disable_hdcp_24m_clk:
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
- disable_hdcp_clk:
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
- return ret;
- }
- static void mtk_hdmi_v2_clk_disable(struct mtk_hdmi *hdmi)
- {
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI]);
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
- clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
- }
- static enum hdmi_hpd_state mtk_hdmi_v2_hpd_pord_status(struct mtk_hdmi *hdmi)
- {
- u8 hpd_pin_sta, pord_pin_sta;
- u32 hpd_status;
- regmap_read(hdmi->regs, HPD_DDC_STATUS, &hpd_status);
- hpd_pin_sta = FIELD_GET(HPD_PIN_STA, hpd_status);
- pord_pin_sta = FIELD_GET(PORD_PIN_STA, hpd_status);
- /*
- * Inform that the cable is plugged in (hpd_pin_sta) so that the
- * sink can be powered on by switching the 5V VBUS as required by
- * the HDMI spec for reading EDID and for HDMI Audio registers to
- * be accessible.
- *
- * PORD detection succeeds only when the cable is plugged in and
- * the sink is powered on: reaching that state means that the
- * communication with the sink can be started.
- *
- * Please note that when the cable is plugged out the HPD pin will
- * be the first one to fall, while PORD may still be in rise state
- * for a few more milliseconds, so we decide HDMI_PLUG_OUT without
- * checking PORD at all (we check only HPD falling for that).
- */
- if (hpd_pin_sta && pord_pin_sta)
- return HDMI_PLUG_IN_AND_SINK_POWER_ON;
- else if (hpd_pin_sta)
- return HDMI_PLUG_IN_ONLY;
- else
- return HDMI_PLUG_OUT;
- }
- static irqreturn_t mtk_hdmi_v2_isr(int irq, void *arg)
- {
- struct mtk_hdmi *hdmi = arg;
- unsigned int irq_sta;
- int ret = IRQ_HANDLED;
- regmap_read(hdmi->regs, TOP_INT_STA00, &irq_sta);
- /* Handle Hotplug Detection interrupts */
- if (irq_sta & HPD_PORD_HWIRQS) {
- /*
- * Disable the HPD/PORD IRQs now and until thread done to
- * avoid interrupt storm that could happen with bad cables
- */
- mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, false);
- ret = IRQ_WAKE_THREAD;
- /* Clear HPD/PORD irqs to avoid unwanted retriggering */
- regmap_write(hdmi->regs, TOP_INT_CLR00, HPD_PORD_HWIRQS);
- regmap_write(hdmi->regs, TOP_INT_CLR00, 0);
- }
- return ret;
- }
- static irqreturn_t __mtk_hdmi_v2_isr_thread(struct mtk_hdmi *hdmi)
- {
- enum hdmi_hpd_state hpd;
- hpd = mtk_hdmi_v2_hpd_pord_status(hdmi);
- if (hpd != hdmi->hpd) {
- struct drm_encoder *encoder = hdmi->bridge.encoder;
- hdmi->hpd = hpd;
- if (encoder && encoder->dev)
- drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev);
- }
- mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
- return IRQ_HANDLED;
- }
- static irqreturn_t mtk_hdmi_v2_isr_thread(int irq, void *arg)
- {
- struct mtk_hdmi *hdmi = arg;
- /*
- * Debounce HDMI monitor HPD status.
- * Empirical testing shows that 30ms is enough wait
- */
- msleep(30);
- return __mtk_hdmi_v2_isr_thread(hdmi);
- }
- static int mtk_hdmi_v2_enable(struct mtk_hdmi *hdmi)
- {
- bool was_active = pm_runtime_active(hdmi->dev);
- int ret;
- ret = pm_runtime_resume_and_get(hdmi->dev);
- if (ret) {
- dev_err(hdmi->dev, "Cannot resume HDMI\n");
- return ret;
- }
- ret = mtk_hdmi_v2_clk_enable(hdmi);
- if (ret) {
- pm_runtime_put(hdmi->dev);
- return ret;
- }
- if (!was_active) {
- mtk_hdmi_v2_hw_reset(hdmi);
- mtk_hdmi_v2_set_sw_hpd(hdmi, true);
- }
- return 0;
- }
- static void mtk_hdmi_v2_disable(struct mtk_hdmi *hdmi)
- {
- mtk_hdmi_v2_clk_disable(hdmi);
- pm_runtime_put_sync(hdmi->dev);
- }
- /*
- * Bridge callbacks
- */
- static int mtk_hdmi_v2_bridge_attach(struct drm_bridge *bridge,
- struct drm_encoder *encoder,
- enum drm_bridge_attach_flags flags)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- int ret;
- if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
- DRM_ERROR("The flag DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied\n");
- return -EINVAL;
- }
- if (hdmi->bridge.next_bridge) {
- ret = drm_bridge_attach(encoder, hdmi->bridge.next_bridge, bridge, flags);
- if (ret)
- return ret;
- }
- ret = mtk_hdmi_v2_enable(hdmi);
- if (ret)
- return ret;
- /* Enable Hotplug and Pord pins internal debouncing */
- regmap_set_bits(hdmi->regs, HPD_DDC_CTRL,
- HPD_DDC_HPD_DBNC_EN | HPD_DDC_PORD_DBNC_EN);
- irq_clear_status_flags(hdmi->irq, IRQ_NOAUTOEN);
- enable_irq(hdmi->irq);
- /*
- * Check if any HDMI monitor was connected before probing this driver
- * and/or attaching the bridge, without debouncing: if so, we want to
- * notify the DRM so that we start outputting an image ASAP.
- * Note that calling the ISR thread function will also perform a HW
- * registers write that enables both the HPD and Pord interrupts.
- */
- __mtk_hdmi_v2_isr_thread(hdmi);
- mtk_hdmi_v2_disable(hdmi);
- return 0;
- }
- static void mtk_hdmi_v2_bridge_detach(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- WARN_ON(pm_runtime_active(hdmi->dev));
- /* The controller is already powered off, just disable irq here */
- disable_irq(hdmi->irq);
- }
- static void mtk_hdmi_v2_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_v2_bridge_pre_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- struct drm_connector_state *conn_state;
- union phy_configure_opts opts = {
- .dp = { .link_rate = hdmi->mode.clock * KILO }
- };
- int ret;
- /* Power on the controller before trying to write to registers */
- ret = mtk_hdmi_v2_enable(hdmi);
- if (WARN_ON(ret))
- return;
- /* Retrieve the connector through the atomic state */
- hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
- conn_state = drm_atomic_get_new_connector_state(state, hdmi->curr_conn);
- if (WARN_ON(!conn_state))
- return;
- /*
- * Preconfigure the HDMI controller and the HDMI PHY at pre_enable
- * stage to make sure that this IP is ready and clocked before the
- * mtk_dpi gets powered on and before it enables the output.
- */
- mtk_hdmi_v2_output_set_display_mode(hdmi, conn_state, &hdmi->mode);
- /* Reconfigure phy clock link with appropriate rate */
- phy_configure(hdmi->phy, &opts);
- /* Power on the PHY here to make sure that DPI_HDMI is clocked */
- phy_power_on(hdmi->phy);
- hdmi->powered = true;
- }
- static void mtk_hdmi_v2_bridge_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- int ret;
- if (WARN_ON(!hdmi->powered))
- return;
- ret = drm_atomic_helper_connector_hdmi_update_infoframes(hdmi->curr_conn, state);
- if (ret)
- dev_err(hdmi->dev, "Could not update infoframes: %d\n", ret);
- mtk_hdmi_v2_hw_vid_mute(hdmi, false);
- /* signal the connect event to audio codec */
- mtk_hdmi_v2_handle_plugged_change(hdmi, true);
- hdmi->enabled = true;
- }
- static void mtk_hdmi_v2_bridge_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- if (!hdmi->enabled)
- return;
- mtk_hdmi_v2_hw_gcp_avmute(hdmi, true);
- msleep(50);
- mtk_hdmi_v2_hw_vid_mute(hdmi, true);
- mtk_hdmi_v2_hw_aud_mute(hdmi, true);
- msleep(50);
- hdmi->enabled = false;
- }
- static void mtk_hdmi_v2_bridge_post_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- if (!hdmi->powered)
- return;
- phy_power_off(hdmi->phy);
- hdmi->powered = false;
- /* signal the disconnect event to audio codec */
- mtk_hdmi_v2_handle_plugged_change(hdmi, false);
- /* Power off */
- mtk_hdmi_v2_disable(hdmi);
- }
- static enum drm_connector_status mtk_hdmi_v2_bridge_detect(struct drm_bridge *bridge,
- struct drm_connector *connector)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- return hdmi->hpd != HDMI_PLUG_OUT ?
- connector_status_connected : connector_status_disconnected;
- }
- static const struct drm_edid *mtk_hdmi_v2_bridge_edid_read(struct drm_bridge *bridge,
- struct drm_connector *connector)
- {
- return drm_edid_read(connector);
- }
- static void mtk_hdmi_v2_hpd_enable(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- int ret;
- ret = mtk_hdmi_v2_enable(hdmi);
- if (ret) {
- dev_err(hdmi->dev, "Cannot power on controller for HPD: %d\n", ret);
- return;
- }
- mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
- }
- static void mtk_hdmi_v2_hpd_disable(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, false);
- mtk_hdmi_v2_disable(hdmi);
- }
- static enum drm_mode_status
- mtk_hdmi_v2_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long tmds_rate)
- {
- if (mode->clock < MTK_HDMI_V2_CLOCK_MIN)
- return MODE_CLOCK_LOW;
- else if (mode->clock > MTK_HDMI_V2_CLOCK_MAX)
- return MODE_CLOCK_HIGH;
- else
- return MODE_OK;
- }
- static int mtk_hdmi_v2_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN_WR | AUD_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
- return 0;
- }
- static int mtk_hdmi_v2_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
- {
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
- regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
- return 0;
- }
- static int mtk_hdmi_v2_set_abist(struct mtk_hdmi *hdmi, bool enable)
- {
- struct drm_display_mode *mode = &hdmi->mode;
- int abist_format = -EINVAL;
- bool interlaced;
- if (!enable) {
- regmap_clear_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_ENABLE);
- return 0;
- }
- if (!mode->hdisplay || !mode->vdisplay)
- return -EINVAL;
- interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
- switch (mode->hdisplay) {
- case 720:
- if (mode->vdisplay == 480)
- abist_format = 2;
- else if (mode->vdisplay == 576)
- abist_format = 11;
- break;
- case 1280:
- if (mode->vdisplay == 720)
- abist_format = 3;
- break;
- case 1440:
- if (mode->vdisplay == 480)
- abist_format = interlaced ? 5 : 9;
- else if (mode->vdisplay == 576)
- abist_format = interlaced ? 14 : 18;
- break;
- case 1920:
- if (mode->vdisplay == 1080)
- abist_format = interlaced ? 4 : 10;
- break;
- case 3840:
- if (mode->vdisplay == 2160)
- abist_format = 25;
- break;
- case 4096:
- if (mode->vdisplay == 2160)
- abist_format = 26;
- break;
- default:
- break;
- }
- if (abist_format < 0)
- return abist_format;
- regmap_update_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_VIDEO_FORMAT,
- FIELD_PREP(HDMI_ABIST_VIDEO_FORMAT, abist_format));
- regmap_set_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_ENABLE);
- return 0;
- }
- static int mtk_hdmi_v2_debug_abist_show(struct seq_file *m, void *arg)
- {
- struct mtk_hdmi *hdmi = m->private;
- bool en;
- u32 val;
- int ret;
- if (!hdmi)
- return -EINVAL;
- ret = regmap_read(hdmi->regs, TOP_CFG00, &val);
- if (ret)
- return ret;
- en = FIELD_GET(HDMI_ABIST_ENABLE, val);
- seq_printf(m, "HDMI Automated Built-In Self Test: %s\n",
- en ? "Enabled" : "Disabled");
- return 0;
- }
- static ssize_t mtk_hdmi_v2_debug_abist_write(struct file *file,
- const char __user *ubuf,
- size_t len, loff_t *offp)
- {
- struct seq_file *m = file->private_data;
- int ret;
- u32 en;
- if (!m || !m->private || *offp)
- return -EINVAL;
- ret = kstrtouint_from_user(ubuf, len, 0, &en);
- if (ret)
- return ret;
- if (en < 0 || en > 1)
- return -EINVAL;
- mtk_hdmi_v2_set_abist((struct mtk_hdmi *)m->private, en);
- return len;
- }
- static int mtk_hdmi_v2_debug_abist_open(struct inode *inode, struct file *file)
- {
- return single_open(file, mtk_hdmi_v2_debug_abist_show, inode->i_private);
- }
- static const struct file_operations mtk_hdmi_debug_abist_fops = {
- .owner = THIS_MODULE,
- .open = mtk_hdmi_v2_debug_abist_open,
- .read = seq_read,
- .write = mtk_hdmi_v2_debug_abist_write,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static void mtk_hdmi_v2_debugfs_init(struct drm_bridge *bridge, struct dentry *root)
- {
- struct mtk_hdmi *dpi = hdmi_ctx_from_bridge(bridge);
- debugfs_create_file("hdmi_abist", 0640, root, dpi, &mtk_hdmi_debug_abist_fops);
- }
- static const struct drm_bridge_funcs mtk_v2_hdmi_bridge_funcs = {
- .attach = mtk_hdmi_v2_bridge_attach,
- .detach = mtk_hdmi_v2_bridge_detach,
- .mode_fixup = mtk_hdmi_bridge_mode_fixup,
- .mode_set = mtk_hdmi_bridge_mode_set,
- .atomic_pre_enable = mtk_hdmi_v2_bridge_pre_enable,
- .atomic_enable = mtk_hdmi_v2_bridge_enable,
- .atomic_disable = mtk_hdmi_v2_bridge_disable,
- .atomic_post_disable = mtk_hdmi_v2_bridge_post_disable,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .detect = mtk_hdmi_v2_bridge_detect,
- .edid_read = mtk_hdmi_v2_bridge_edid_read,
- .hpd_enable = mtk_hdmi_v2_hpd_enable,
- .hpd_disable = mtk_hdmi_v2_hpd_disable,
- .hdmi_tmds_char_rate_valid = mtk_hdmi_v2_hdmi_tmds_char_rate_valid,
- .hdmi_clear_audio_infoframe = mtk_hdmi_v2_hdmi_clear_audio_infoframe,
- .hdmi_write_audio_infoframe = mtk_hdmi_v2_hdmi_write_audio_infoframe,
- .hdmi_clear_avi_infoframe = mtk_hdmi_v2_hdmi_clear_avi_infoframe,
- .hdmi_write_avi_infoframe = mtk_hdmi_v2_hdmi_write_avi_infoframe,
- .hdmi_clear_spd_infoframe = mtk_hdmi_v2_hdmi_clear_spd_infoframe,
- .hdmi_write_spd_infoframe = mtk_hdmi_v2_hdmi_write_spd_infoframe,
- .hdmi_clear_hdmi_infoframe = mtk_hdmi_v2_hdmi_clear_hdmi_infoframe,
- .hdmi_write_hdmi_infoframe = mtk_hdmi_v2_hdmi_write_hdmi_infoframe,
- .debugfs_init = mtk_hdmi_v2_debugfs_init,
- };
- /*
- * HDMI audio codec callbacks
- */
- static int mtk_hdmi_v2_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);
- bool plugged;
- if (!hdmi)
- return -ENODEV;
- mtk_hdmi_audio_set_plugged_cb(hdmi, fn, codec_dev);
- plugged = (hdmi->hpd == HDMI_PLUG_IN_AND_SINK_POWER_ON);
- mtk_hdmi_v2_handle_plugged_change(hdmi, plugged);
- return 0;
- }
- static int mtk_hdmi_v2_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);
- if (hdmi->audio_enable) {
- mtk_hdmi_audio_params(hdmi, daifmt, params);
- mtk_hdmi_v2_aud_output_config(hdmi, &hdmi->mode);
- }
- return 0;
- }
- static int mtk_hdmi_v2_audio_startup(struct device *dev, void *data)
- {
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- mtk_hdmi_v2_hw_aud_enable(hdmi, true);
- hdmi->audio_enable = true;
- return 0;
- }
- static void mtk_hdmi_v2_audio_shutdown(struct device *dev, void *data)
- {
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- hdmi->audio_enable = false;
- mtk_hdmi_v2_hw_aud_enable(hdmi, false);
- }
- static int mtk_hdmi_v2_audio_mute(struct device *dev, void *data, bool enable, int dir)
- {
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- mtk_hdmi_v2_hw_aud_mute(hdmi, enable);
- return 0;
- }
- static const struct hdmi_codec_ops mtk_hdmi_v2_audio_codec_ops = {
- .hw_params = mtk_hdmi_v2_audio_hw_params,
- .audio_startup = mtk_hdmi_v2_audio_startup,
- .audio_shutdown = mtk_hdmi_v2_audio_shutdown,
- .mute_stream = mtk_hdmi_v2_audio_mute,
- .get_eld = mtk_hdmi_audio_get_eld,
- .hook_plugged_cb = mtk_hdmi_v2_audio_hook_plugged_cb,
- };
- static __maybe_unused int mtk_hdmi_v2_suspend(struct device *dev)
- {
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- mtk_hdmi_v2_disable(hdmi);
- return 0;
- }
- static __maybe_unused int mtk_hdmi_v2_resume(struct device *dev)
- {
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- return mtk_hdmi_v2_enable(hdmi);
- }
- static SIMPLE_DEV_PM_OPS(mtk_hdmi_v2_pm_ops, mtk_hdmi_v2_suspend, mtk_hdmi_v2_resume);
- static const struct mtk_hdmi_ver_conf mtk_hdmi_conf_v2 = {
- .bridge_funcs = &mtk_v2_hdmi_bridge_funcs,
- .codec_ops = &mtk_hdmi_v2_audio_codec_ops,
- .mtk_hdmi_clock_names = mtk_hdmi_v2_clk_names,
- .num_clocks = MTK_HDMI_V2_CLK_COUNT,
- .interlace_allowed = true,
- };
- static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8188 = {
- .ver_conf = &mtk_hdmi_conf_v2,
- .reg_hdmi_tx_cfg = HDMITX_CONFIG_MT8188
- };
- static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8195 = {
- .ver_conf = &mtk_hdmi_conf_v2,
- .reg_hdmi_tx_cfg = HDMITX_CONFIG_MT8195
- };
- static int mtk_hdmi_v2_probe(struct platform_device *pdev)
- {
- struct mtk_hdmi *hdmi;
- int ret;
- /* Populate HDMI sub-devices if present */
- ret = devm_of_platform_populate(&pdev->dev);
- if (ret)
- return ret;
- hdmi = mtk_hdmi_common_probe(pdev);
- if (IS_ERR(hdmi))
- return PTR_ERR(hdmi);
- hdmi->hpd = HDMI_PLUG_OUT;
- /* Disable all HW interrupts at probe stage */
- mtk_hdmi_v2_hwirq_disable(hdmi);
- /*
- * In case bootloader leaves HDMI enabled before booting, make
- * sure that any interrupt that was left is cleared by setting
- * all bits in the INT_CLR registers for all 32+19 interrupts.
- */
- regmap_write(hdmi->regs, TOP_INT_CLR00, GENMASK(31, 0));
- regmap_write(hdmi->regs, TOP_INT_CLR01, GENMASK(18, 0));
- /* Restore interrupt clearing registers to zero */
- regmap_write(hdmi->regs, TOP_INT_CLR00, 0);
- regmap_write(hdmi->regs, TOP_INT_CLR01, 0);
- /*
- * Install the ISR but keep it disabled: as the interrupts are
- * being set up in the .bridge_attach() callback which will
- * enable both the right HW IRQs and the ISR.
- */
- irq_set_status_flags(hdmi->irq, IRQ_NOAUTOEN);
- ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq, mtk_hdmi_v2_isr,
- mtk_hdmi_v2_isr_thread,
- IRQ_TYPE_LEVEL_HIGH,
- dev_name(&pdev->dev), hdmi);
- if (ret)
- return dev_err_probe(&pdev->dev, ret, "Cannot request IRQ\n");
- ret = devm_pm_runtime_enable(&pdev->dev);
- if (ret)
- return dev_err_probe(&pdev->dev, ret, "Cannot enable Runtime PM\n");
- return 0;
- }
- static void mtk_hdmi_v2_remove(struct platform_device *pdev)
- {
- struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
- i2c_put_adapter(hdmi->ddc_adpt);
- }
- static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = {
- { .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 },
- { .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids);
- static struct platform_driver mtk_hdmi_v2_driver = {
- .probe = mtk_hdmi_v2_probe,
- .remove = mtk_hdmi_v2_remove,
- .driver = {
- .name = "mediatek-drm-hdmi-v2",
- .of_match_table = mtk_drm_hdmi_v2_of_ids,
- .pm = &mtk_hdmi_v2_pm_ops,
- },
- };
- module_platform_driver(mtk_hdmi_v2_driver);
- MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>>");
- MODULE_DESCRIPTION("MediaTek HDMIv2 Driver");
- MODULE_LICENSE("GPL");
- MODULE_IMPORT_NS("DRM_MTK_HDMI");
|