summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdt/ausoc.c46
-rw-r--r--sys/dev/fdt/fdt_clock.c28
-rw-r--r--sys/dev/fdt/fdtvar.h4
-rw-r--r--sys/dev/ic/dw_hdmi.c308
-rw-r--r--sys/dev/ic/dw_hdmi.h39
-rw-r--r--sys/dev/ic/dw_hdmi_phy.c401
6 files changed, 780 insertions, 46 deletions
diff --git a/sys/dev/fdt/ausoc.c b/sys/dev/fdt/ausoc.c
index 3bb5245b2a2..88ea54afba1 100644
--- a/sys/dev/fdt/ausoc.c
+++ b/sys/dev/fdt/ausoc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki Exp $ */
+/* $NetBSD: ausoc.c,v 1.4.2.1 2019/11/16 16:48:25 martin Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ausoc.c,v 1.4.2.1 2019/11/16 16:48:25 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -121,8 +121,22 @@ ausoc_set_format(void *priv, int setmode,
audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
{
struct ausoc_link * const link = priv;
+ const audio_params_t *params = (setmode & AUMODE_PLAY) != 0 ?
+ play : rec;
int error;
+ if (link->link_mclk_fs) {
+ const u_int rate = params->sample_rate * link->link_mclk_fs;
+ error = audio_dai_set_sysclk(link->link_codec, rate,
+ AUDIO_DAI_CLOCK_IN);
+ if (error)
+ return error;
+ error = audio_dai_set_sysclk(link->link_cpu, rate,
+ AUDIO_DAI_CLOCK_OUT);
+ if (error)
+ return error;
+ }
+
error = audio_dai_mi_set_format(link->link_cpu, setmode,
play, rec, pfil, rfil);
if (error)
@@ -246,20 +260,8 @@ ausoc_trigger_output(void *priv, void *start, void *end, int blksize,
void (*intr)(void *), void *intrarg, const audio_params_t *params)
{
struct ausoc_link * const link = priv;
- u_int n, rate;
int error;
-
- if (link->link_mclk_fs) {
- rate = params->sample_rate * link->link_mclk_fs;
- error = audio_dai_set_sysclk(link->link_codec, rate,
- AUDIO_DAI_CLOCK_IN);
- if (error)
- goto failed;
- error = audio_dai_set_sysclk(link->link_cpu, rate,
- AUDIO_DAI_CLOCK_OUT);
- if (error)
- goto failed;
- }
+ u_int n;
for (n = 0; n < link->link_naux; n++) {
error = audio_dai_trigger(link->link_aux[n], start, end,
@@ -285,20 +287,8 @@ ausoc_trigger_input(void *priv, void *start, void *end, int blksize,
void (*intr)(void *), void *intrarg, const audio_params_t *params)
{
struct ausoc_link * const link = priv;
- u_int n, rate;
int error;
-
- if (link->link_mclk_fs) {
- rate = params->sample_rate * link->link_mclk_fs;
- error = audio_dai_set_sysclk(link->link_codec, rate,
- AUDIO_DAI_CLOCK_IN);
- if (error)
- goto failed;
- error = audio_dai_set_sysclk(link->link_cpu, rate,
- AUDIO_DAI_CLOCK_OUT);
- if (error)
- goto failed;
- }
+ u_int n;
for (n = 0; n < link->link_naux; n++) {
error = audio_dai_trigger(link->link_aux[n], start, end,
diff --git a/sys/dev/fdt/fdt_clock.c b/sys/dev/fdt/fdt_clock.c
index 28db66c1522..0f2503b88e6 100644
--- a/sys/dev/fdt/fdt_clock.c
+++ b/sys/dev/fdt/fdt_clock.c
@@ -1,4 +1,4 @@
-/* $NetBSD: fdt_clock.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $ */
+/* $NetBSD: fdt_clock.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -161,6 +161,30 @@ fdtbus_clock_get(int phandle, const char *clkname)
return fdtbus_clock_get_prop(phandle, clkname, "clock-names");
}
+int
+fdtbus_clock_enable(int phandle, const char *clkname, bool required)
+{
+ struct clk *clk;
+
+ clk = fdtbus_clock_get(phandle, clkname);
+ if (clk == NULL)
+ return required ? ENOENT : 0;
+
+ return clk_enable(clk);
+}
+
+int
+fdtbus_clock_enable_index(int phandle, u_int index, bool required)
+{
+ struct clk *clk;
+
+ clk = fdtbus_clock_get_index(phandle, index);
+ if (clk == NULL)
+ return required ? ENOENT : 0;
+
+ return clk_enable(clk);
+}
+
/*
* Search the DT for a clock by "clock-output-names" property.
*
diff --git a/sys/dev/fdt/fdtvar.h b/sys/dev/fdt/fdtvar.h
index a0c4b7b374e..dc6d2665d7f 100644
--- a/sys/dev/fdt/fdtvar.h
+++ b/sys/dev/fdt/fdtvar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: fdtvar.h,v 1.52.2.1 2019/10/03 17:23:11 martin Exp $ */
+/* $NetBSD: fdtvar.h,v 1.52.2.2 2019/11/16 16:48:25 martin Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
@@ -345,6 +345,8 @@ struct clk * fdtbus_clock_get(int, const char *);
struct clk * fdtbus_clock_get_index(int, u_int);
struct clk * fdtbus_clock_byname(const char *);
void fdtbus_clock_assign(int);
+int fdtbus_clock_enable(int, const char *, bool);
+int fdtbus_clock_enable_index(int, u_int, bool);
struct fdtbus_reset *fdtbus_reset_get(int, const char *);
struct fdtbus_reset *fdtbus_reset_get_index(int, u_int);
diff --git a/sys/dev/ic/dw_hdmi.c b/sys/dev/ic/dw_hdmi.c
index ac374702636..41198497c38 100644
--- a/sys/dev/ic/dw_hdmi.c
+++ b/sys/dev/ic/dw_hdmi.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */
+/* $NetBSD: dw_hdmi.c,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $ */
/*-
* Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -45,11 +45,19 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $")
#include <dev/videomode/videomode.h>
#include <dev/videomode/edidvar.h>
+#include <dev/audio/audio_dai.h>
+
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#define HDMI_DESIGN_ID 0x0000
+#define HDMI_REVISION_ID 0x0001
+#define HDMI_CONFIG0_ID 0x0004
+#define HDMI_CONFIG0_ID_AUDI2S __BIT(4)
+#define HDMI_CONFIG2_ID 0x0006
+
#define HDMI_IH_I2CM_STAT0 0x0105
#define HDMI_IH_I2CM_STAT0_DONE __BIT(1)
#define HDMI_IH_I2CM_STAT0_ERROR __BIT(0)
@@ -127,6 +135,49 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $")
#define HDMI_FC_CH1PREAM_DEFAULT 0x16
#define HDMI_FC_CH2PREAM 0x1016
#define HDMI_FC_CH2PREAM_DEFAULT 0x21
+#define HDMI_FC_AUDCONF0 0x1025
+#define HDMI_FC_AUDCONF1 0x1026
+#define HDMI_FC_AUDCONF2 0x1027
+#define HDMI_FC_AUDCONF3 0x1028
+
+#define HDMI_PHY_CONF0 0x3000
+#define HDMI_PHY_CONF0_PDZ __BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS __BIT(6)
+#define HDMI_PHY_CONF0_SVSRET __BIT(5)
+#define HDMI_PHY_CONF0_PDDQ __BIT(4)
+#define HDMI_PHY_CONF0_TXPWRON __BIT(3)
+#define HDMI_PHY_CONF0_ENHPDRXSENSE __BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL __BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF __BIT(0)
+#define HDMI_PHY_STAT0 0x3004
+#define HDMI_PHY_STAT0_RX_SENSE_3 __BIT(7)
+#define HDMI_PHY_STAT0_RX_SENSE_2 __BIT(6)
+#define HDMI_PHY_STAT0_RX_SENSE_1 __BIT(5)
+#define HDMI_PHY_STAT0_RX_SENSE_0 __BIT(4)
+#define HDMI_PHY_STAT0_HPD __BIT(1)
+#define HDMI_PHY_STAT0_TX_PHY_LOCK __BIT(0)
+
+#define HDMI_AUD_CONF0 0x3100
+#define HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST __BIT(7)
+#define HDMI_AUD_CONF0_I2S_SELECT __BIT(5)
+#define HDMI_AUD_CONF0_I2S_IN_EN __BITS(3,0)
+#define HDMI_AUD_CONF1 0x3101
+#define HDMI_AUD_CONF1_I2S_WIDTH __BITS(4,0)
+#define HDMI_AUD_INT 0x3102
+#define HDMI_AUD_CONF2 0x3103
+#define HDMI_AUD_CONF2_INSERT_PCUV __BIT(2)
+#define HDMI_AUD_CONF2_NLPCM __BIT(1)
+#define HDMI_AUD_CONF2_HBR __BIT(0)
+#define HDMI_AUD_INT1 0x3104
+
+#define HDMI_AUD_N1 0x3200
+#define HDMI_AUD_N2 0x3201
+#define HDMI_AUD_N3 0x3202
+#define HDMI_AUD_CTS1 0x3203
+#define HDMI_AUD_CTS2 0x3204
+#define HDMI_AUD_CTS3 0x3205
+#define HDMI_AUD_INPUTCLKFS 0x3206
+#define HDMI_AUD_INPUTCLKFS_IFSFACTOR __BITS(2,0)
#define HDMI_MC_CLKDIS 0x4001
#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE __BIT(6)
@@ -143,6 +194,8 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $")
#define HDMI_MC_SWRSTZREQ_PIXELSWRST_REQ __BIT(0)
#define HDMI_MC_FLOWCTRL 0x4004
#define HDMI_MC_PHYRSTZ 0x4005
+#define HDMI_MC_PHYRSTZ_ASSERT __BIT(0)
+#define HDMI_MC_PHYRSTZ_DEASSERT 0
#define HDMI_MC_LOCKONCLOCK 0x4006
#define HDMI_MC_HEACPHY_RST 0x4007
@@ -187,6 +240,16 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $")
#define HDMI_I2CM_SOFTRSTZ_I2C_SOFTRST __BIT(0)
#define HDMI_I2CM_SEGPTR 0x7e0a
+enum dwhdmi_dai_mixer_ctrl {
+ DWHDMI_DAI_OUTPUT_CLASS,
+ DWHDMI_DAI_INPUT_CLASS,
+
+ DWHDMI_DAI_OUTPUT_MASTER_VOLUME,
+ DWHDMI_DAI_INPUT_DAC_VOLUME,
+
+ DWHDMI_DAI_MIXER_CTRL_LAST
+};
+
static int
dwhdmi_ddc_acquire_bus(void *priv, int flags)
{
@@ -410,9 +473,8 @@ dwhdmi_fc_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
static void
dwhdmi_mc_init(struct dwhdmi_softc *sc)
{
- struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector;
uint8_t val;
- u_int n;
+ u_int n, iter;
/* Bypass colour space converter */
dwhdmi_write(sc, HDMI_MC_FLOWCTRL, 0);
@@ -422,16 +484,16 @@ dwhdmi_mc_init(struct dwhdmi_softc *sc)
HDMI_MC_CLKDIS_CECCLK_DISABLE |
HDMI_MC_CLKDIS_CSCCLK_DISABLE |
HDMI_MC_CLKDIS_PREPCLK_DISABLE;
- if (!dwhdmi_connector->monitor_audio)
- val |= HDMI_MC_CLKDIS_AUDCLK_DISABLE;
dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
/* Soft reset TMDS */
val = 0xff & ~HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ;
dwhdmi_write(sc, HDMI_MC_SWRSTZREQ, val);
+ iter = sc->sc_version == 0x130a ? 4 : 1;
+
val = dwhdmi_read(sc, HDMI_FC_INVIDCONF);
- for (n = 0; n < 4; n++)
+ for (n = 0; n < iter; n++)
dwhdmi_write(sc, HDMI_FC_INVIDCONF, val);
}
@@ -442,6 +504,59 @@ dwhdmi_mc_disable(struct dwhdmi_softc *sc)
dwhdmi_write(sc, HDMI_MC_CLKDIS, 0xff);
}
+static void
+dwhdmi_audio_init(struct dwhdmi_softc *sc)
+{
+ uint8_t val;
+ u_int n;
+
+ /* The following values are for 48 kHz */
+ switch (sc->sc_curmode.clock) {
+ case 25170:
+ n = 6864;
+ break;
+ case 74170:
+ n = 11648;
+ break;
+ case 148350:
+ n = 5824;
+ break;
+ default:
+ n = 6144;
+ break;
+ }
+
+ /* Use automatic CTS generation */
+ dwhdmi_write(sc, HDMI_AUD_CTS1, 0);
+ dwhdmi_write(sc, HDMI_AUD_CTS2, 0);
+ dwhdmi_write(sc, HDMI_AUD_CTS3, 0);
+
+ /* Set N factor for audio clock regeneration */
+ dwhdmi_write(sc, HDMI_AUD_N1, n & 0xff);
+ dwhdmi_write(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
+ dwhdmi_write(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
+
+ val = dwhdmi_read(sc, HDMI_AUD_CONF0);
+ val |= HDMI_AUD_CONF0_I2S_SELECT; /* XXX i2s mode */
+ val &= ~HDMI_AUD_CONF0_I2S_IN_EN;
+ val |= __SHIFTIN(1, HDMI_AUD_CONF0_I2S_IN_EN); /* XXX 2ch */
+ dwhdmi_write(sc, HDMI_AUD_CONF0, val);
+
+ val = __SHIFTIN(16, HDMI_AUD_CONF1_I2S_WIDTH);
+ dwhdmi_write(sc, HDMI_AUD_CONF1, val);
+
+ dwhdmi_write(sc, HDMI_AUD_INPUTCLKFS, 4); /* XXX 64 FS */
+
+ dwhdmi_write(sc, HDMI_FC_AUDCONF0, 1 << 4); /* XXX 2ch */
+ dwhdmi_write(sc, HDMI_FC_AUDCONF1, 0);
+ dwhdmi_write(sc, HDMI_FC_AUDCONF2, 0);
+ dwhdmi_write(sc, HDMI_FC_AUDCONF3, 0);
+
+ val = dwhdmi_read(sc, HDMI_MC_CLKDIS);
+ val &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
+ dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
+}
+
static enum drm_connector_status
dwhdmi_connector_detect(struct drm_connector *connector, bool force)
{
@@ -479,7 +594,7 @@ dwhdmi_connector_get_modes(struct drm_connector *connector)
memset(edid, 0, sizeof(edid));
for (block = 0; block < 4; block++) {
- error = ddc_read_edid_block(&sc->sc_ic,
+ error = ddc_read_edid_block(sc->sc_ic,
&edid[block * EDID_LENGTH], EDID_LENGTH, block);
if (error != 0)
break;
@@ -569,6 +684,9 @@ dwhdmi_bridge_enable(struct drm_bridge *bridge)
dwhdmi_tx_init(sc);
dwhdmi_mc_init(sc);
+
+ if (sc->sc_connector.monitor_audio)
+ dwhdmi_audio_init(sc);
}
static void
@@ -621,10 +739,136 @@ static const struct drm_bridge_funcs dwhdmi_bridge_funcs = {
.mode_fixup = dwhdmi_bridge_mode_fixup,
};
+static int
+dwhdmi_dai_set_format(audio_dai_tag_t dai, u_int format)
+{
+ return 0;
+}
+
+static int
+dwhdmi_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux)
+{
+ /* Not supported */
+ return 0;
+}
+
+static void
+dwhdmi_audio_swvol_codec(audio_filter_arg_t *arg)
+{
+ struct dwhdmi_softc * const sc = arg->context;
+ const aint_t *src;
+ aint_t *dst;
+ u_int sample_count;
+ u_int i;
+
+ src = arg->src;
+ dst = arg->dst;
+ sample_count = arg->count * arg->srcfmt->channels;
+ for (i = 0; i < sample_count; i++) {
+ aint2_t v = (aint2_t)(*src++);
+ v = v * sc->sc_swvol / 255;
+ *dst++ = (aint_t)v;
+ }
+}
+
+static int
+dwhdmi_audio_set_format(void *priv, int setmode,
+ const audio_params_t *play, const audio_params_t *rec,
+ audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
+{
+ struct dwhdmi_softc * const sc = priv;
+
+ pfil->codec = dwhdmi_audio_swvol_codec;
+ pfil->context = sc;
+
+ return 0;
+}
+
+static int
+dwhdmi_audio_set_port(void *priv, mixer_ctrl_t *mc)
+{
+ struct dwhdmi_softc * const sc = priv;
+
+ switch (mc->dev) {
+ case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+ case DWHDMI_DAI_INPUT_DAC_VOLUME:
+ sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
+ return 0;
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+dwhdmi_audio_get_port(void *priv, mixer_ctrl_t *mc)
+{
+ struct dwhdmi_softc * const sc = priv;
+
+ switch (mc->dev) {
+ case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+ case DWHDMI_DAI_INPUT_DAC_VOLUME:
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_swvol;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_swvol;
+ return 0;
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+dwhdmi_audio_query_devinfo(void *priv, mixer_devinfo_t *di)
+{
+ switch (di->index) {
+ case DWHDMI_DAI_OUTPUT_CLASS:
+ di->mixer_class = di->index;
+ strcpy(di->label.name, AudioCoutputs);
+ di->type = AUDIO_MIXER_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case DWHDMI_DAI_INPUT_CLASS:
+ di->mixer_class = di->index;
+ strcpy(di->label.name, AudioCinputs);
+ di->type = AUDIO_MIXER_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+ di->mixer_class = DWHDMI_DAI_OUTPUT_CLASS;
+ strcpy(di->label.name, AudioNmaster);
+ di->un.v.delta = 1;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ di->type = AUDIO_MIXER_VALUE;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case DWHDMI_DAI_INPUT_DAC_VOLUME:
+ di->mixer_class = DWHDMI_DAI_INPUT_CLASS;
+ strcpy(di->label.name, AudioNdac);
+ di->un.v.delta = 1;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ di->type = AUDIO_MIXER_VALUE;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static const struct audio_hw_if dwhdmi_dai_hw_if = {
+ .set_format = dwhdmi_audio_set_format,
+ .set_port = dwhdmi_audio_set_port,
+ .get_port = dwhdmi_audio_get_port,
+ .query_devinfo = dwhdmi_audio_query_devinfo,
+};
+
int
dwhdmi_attach(struct dwhdmi_softc *sc)
{
- struct i2c_controller *ic = &sc->sc_ic;
+ uint8_t val;
if (sc->sc_reg_width != 1 && sc->sc_reg_width != 4) {
aprint_error_dev(sc->sc_dev, "unsupported register width %d\n", sc->sc_reg_width);
@@ -633,10 +877,48 @@ dwhdmi_attach(struct dwhdmi_softc *sc)
mutex_init(&sc->sc_ic_lock, MUTEX_DEFAULT, IPL_NONE);
- ic->ic_cookie = sc;
- ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus;
- ic->ic_release_bus = dwhdmi_ddc_release_bus;
- ic->ic_exec = dwhdmi_ddc_exec;
+ sc->sc_version = dwhdmi_read(sc, HDMI_DESIGN_ID);
+ sc->sc_version <<= 8;
+ sc->sc_version |= dwhdmi_read(sc, HDMI_REVISION_ID);
+
+ sc->sc_phytype = dwhdmi_read(sc, HDMI_CONFIG2_ID);
+
+ aprint_normal_dev(sc->sc_dev, "version %x.%03x, phytype 0x%02x\n",
+ sc->sc_version >> 12, sc->sc_version & 0xfff,
+ sc->sc_phytype);
+
+ sc->sc_swvol = 255;
+
+ /*
+ * If a DDC i2c bus tag is provided by the caller, use it. Otherwise,
+ * use the I2C master built-in to DWC HDMI.
+ */
+ if (sc->sc_ic == NULL) {
+ struct i2c_controller *ic = &sc->sc_ic_builtin;
+ ic->ic_cookie = sc;
+ ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus;
+ ic->ic_release_bus = dwhdmi_ddc_release_bus;
+ ic->ic_exec = dwhdmi_ddc_exec;
+ sc->sc_ic = ic;
+ }
+
+ /*
+ * Enable HPD on internal PHY
+ */
+ if ((sc->sc_flags & DWHDMI_USE_INTERNAL_PHY) != 0) {
+ val = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ val |= HDMI_PHY_CONF0_ENHPDRXSENSE;
+ dwhdmi_write(sc, HDMI_PHY_CONF0, val);
+ }
+
+ /*
+ * Initialize audio DAI
+ */
+ sc->sc_dai.dai_set_format = dwhdmi_dai_set_format;
+ sc->sc_dai.dai_add_device = dwhdmi_dai_add_device;
+ sc->sc_dai.dai_hw_if = &dwhdmi_dai_hw_if;
+ sc->sc_dai.dai_dev = sc->sc_dev;
+ sc->sc_dai.dai_priv = sc;
return 0;
}
diff --git a/sys/dev/ic/dw_hdmi.h b/sys/dev/ic/dw_hdmi.h
index 52782b73b4e..9c6fb0580bd 100644
--- a/sys/dev/ic/dw_hdmi.h
+++ b/sys/dev/ic/dw_hdmi.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dw_hdmi.h,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */
+/* $NetBSD: dw_hdmi.h,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $ */
/*-
* Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
@@ -32,6 +32,8 @@
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/ddcreg.h>
+#include <dev/audio/audio_dai.h>
+
#include <drm/drmP.h>
struct dwhdmi_softc;
@@ -44,20 +46,46 @@ struct dwhdmi_connector {
bool monitor_audio;
};
+struct dwhdmi_phy_config {
+ u_int pixel_clock;
+ uint32_t sym;
+ uint32_t term;
+ uint32_t vlev;
+};
+
+struct dwhdmi_mpll_config {
+ u_int pixel_clock;
+ uint32_t cpce;
+ uint32_t gmp;
+ uint32_t curr;
+};
+
struct dwhdmi_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
u_int sc_reg_width;
+ u_int sc_flags;
+#define DWHDMI_USE_INTERNAL_PHY __BIT(0)
- struct i2c_controller sc_ic;
+ u_int sc_phytype;
+ u_int sc_version;
+
+ i2c_tag_t sc_ic;
kmutex_t sc_ic_lock;
+ struct i2c_controller sc_ic_builtin;
+
+ struct audio_dai_device sc_dai;
+ uint8_t sc_swvol;
struct dwhdmi_connector sc_connector;
struct drm_bridge sc_bridge;
struct drm_display_mode sc_curmode;
+ const struct dwhdmi_mpll_config *sc_mpll_config;
+ const struct dwhdmi_phy_config *sc_phy_config;
+
enum drm_connector_status (*sc_detect)(struct dwhdmi_softc *, bool);
void (*sc_enable)(struct dwhdmi_softc *);
void (*sc_disable)(struct dwhdmi_softc *);
@@ -74,4 +102,11 @@ int dwhdmi_bind(struct dwhdmi_softc *, struct drm_encoder *);
uint8_t dwhdmi_read(struct dwhdmi_softc *, bus_size_t);
void dwhdmi_write(struct dwhdmi_softc *, bus_size_t, uint8_t);
+enum drm_connector_status dwhdmi_phy_detect(struct dwhdmi_softc *, bool);
+void dwhdmi_phy_enable(struct dwhdmi_softc *);
+void dwhdmi_phy_disable(struct dwhdmi_softc *);
+void dwhdmi_phy_mode_set(struct dwhdmi_softc *,
+ struct drm_display_mode *,
+ struct drm_display_mode *);
+
#endif /* !_DEV_IC_DWHDMI_H */
diff --git a/sys/dev/ic/dw_hdmi_phy.c b/sys/dev/ic/dw_hdmi_phy.c
new file mode 100644
index 00000000000..bf29676fcf2
--- /dev/null
+++ b/sys/dev/ic/dw_hdmi_phy.c
@@ -0,0 +1,401 @@
+/* $NetBSD: dw_hdmi_phy.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $ */
+
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: dw_hdmi_phy.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $");
+
+#include <sys/param.h>
+
+#include <drm/drmP.h>
+
+#include <dev/ic/dw_hdmi.h>
+
+#define HDMI_IH_PHY_STAT0 0x0104
+#define HDMI_IH_PHY_STAT0_HPD (1 << 0)
+#define HDMI_IH_I2CMPHY_STAT0 0x0108
+#define HDMI_IH_I2CMPHY_STAT0_DONE (1 << 1)
+#define HDMI_IH_I2CMPHY_STAT0_ERROR (1 << 0)
+
+#define HDMI_PHY_CONF0 0x3000
+#define HDMI_PHY_CONF0_PDZ_MASK 0x80
+#define HDMI_PHY_CONF0_PDZ_OFFSET 7
+#define HDMI_PHY_CONF0_ENTMDS_MASK 0x40
+#define HDMI_PHY_CONF0_ENTMDS_OFFSET 6
+#define HDMI_PHY_CONF0_SVSRET_MASK 0x20
+#define HDMI_PHY_CONF0_SVSRET_OFFSET 5
+#define HDMI_PHY_CONF0_GEN2_PDDQ_MASK 0x10
+#define HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET 4
+#define HDMI_PHY_CONF0_GEN2_TXPWRON_MASK 0x8
+#define HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET 3
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK 0x4
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET 2
+#define HDMI_PHY_CONF0_SELDATAENPOL_MASK 0x2
+#define HDMI_PHY_CONF0_SELDATAENPOL_OFFSET 1
+#define HDMI_PHY_CONF0_SELDIPIF_MASK 0x1
+#define HDMI_PHY_CONF0_SELDIPIF_OFFSET 0
+#define HDMI_PHY_TST0 0x3001
+#define HDMI_PHY_TST0_TSTCLR_MASK 0x20
+#define HDMI_PHY_TST0_TSTCLR_OFFSET 5
+#define HDMI_PHY_TST0_TSTEN_MASK 0x10
+#define HDMI_PHY_TST0_TSTEN_OFFSET 4
+#define HDMI_PHY_TST0_TSTCLK_MASK 0x1
+#define HDMI_PHY_TST0_TSTCLK_OFFSET 0
+#define HDMI_PHY_TST1 0x3002
+#define HDMI_PHY_TST2 0x3003
+#define HDMI_PHY_STAT0 0x3004
+#define HDMI_PHY_STAT0_RX_SENSE3 0x80
+#define HDMI_PHY_STAT0_RX_SENSE2 0x40
+#define HDMI_PHY_STAT0_RX_SENSE1 0x20
+#define HDMI_PHY_STAT0_RX_SENSE0 0x10
+#define HDMI_PHY_STAT0_RX_SENSE 0xf0
+#define HDMI_PHY_STAT0_HPD 0x02
+#define HDMI_PHY_TX_PHY_LOCK 0x01
+#define HDMI_PHY_INT0 0x3005
+#define HDMI_PHY_MASK0 0x3006
+#define HDMI_PHY_POL0 0x3007
+#define HDMI_PHY_POL0_HPD 0x02
+
+/* HDMI Master PHY Registers */
+#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY 0x49
+#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021
+#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022
+#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023
+#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024
+#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025
+#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define HDMI_PHY_I2CM_OPERATION_ADDR_READ 0x1
+#define HDMI_PHY_I2CM_INT_ADDR 0x3027
+#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028
+#define HDMI_PHY_I2CM_DIV_ADDR 0x3029
+#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032
+
+#define HDMI_MC_FLOWCTRL 0x4004
+#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK 0x1
+#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH 0x1
+#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS 0x0
+#define HDMI_MC_PHYRSTZ 0x4005
+#define HDMI_MC_PHYRSTZ_ASSERT 0x0
+#define HDMI_MC_PHYRSTZ_DEASSERT 0x1
+#define HDMI_MC_HEACPHY_RST 0x4007
+#define HDMI_MC_HEACPHY_RST_ASSERT 0x1
+#define HDMI_MC_HEACPHY_RST_DEASSERT 0x0
+
+/* HDMI PHY register with access through I2C */
+#define HDMI_PHY_I2C_CKCALCTRL 0x5
+#define CKCALCTRL_OVERRIDE (1 << 15)
+#define HDMI_PHY_I2C_CPCE_CTRL 0x6
+#define CPCE_CTRL_45_25 ((3 << 7) | (3 << 5))
+#define CPCE_CTRL_92_50 ((2 << 7) | (2 << 5))
+#define CPCE_CTRL_185 ((1 << 7) | (1 << 5))
+#define CPCE_CTRL_370 ((0 << 7) | (0 << 5))
+#define HDMI_PHY_I2C_CKSYMTXCTRL 0x9
+#define CKSYMTXCTRL_OVERRIDE (1 << 15)
+#define CKSYMTXCTRL_TX_SYMON (1 << 3)
+#define CKSYMTXCTRL_TX_TRAON (1 << 2)
+#define CKSYMTXCTRL_TX_TRBON (1 << 1)
+#define CKSYMTXCTRL_TX_CK_SYMON (1 << 0)
+#define HDMI_PHY_I2C_VLEVCTRL 0x0E
+#define HDMI_PHY_I2C_CURRCTRL 0x10
+#define HDMI_PHY_I2C_PLLPHBYCTRL 0x13
+#define VLEVCTRL_TX_LVL(x) ((x) << 5)
+#define VLEVCTRL_CK_LVL(x) (x)
+#define HDMI_PHY_I2C_GMPCTRL 0x15
+#define GMPCTRL_45_25 0x00
+#define GMPCTRL_92_50 0x05
+#define GMPCTRL_185 0x0a
+#define GMPCTRL_370 0x0f
+#define HDMI_PHY_I2C_MSM_CTRL 0x17
+#define MSM_CTRL_FB_CLK (0x3 << 1)
+#define HDMI_PHY_I2C_TXTERM 0x19
+#define TXTERM_133 0x5
+
+static void
+dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc *sc, int msec)
+{
+ uint8_t val;
+
+ val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) &
+ (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
+ while (val == 0) {
+ delay(1000);
+ msec -= 10;
+ if (msec <= 0)
+ return;
+ val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) &
+ (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
+ }
+}
+
+static void
+dwhdmi_phy_i2c_write(struct dwhdmi_softc *sc, unsigned short data,
+ unsigned char addr)
+{
+
+ /* clear DONE and ERROR flags */
+ dwhdmi_write(sc, HDMI_IH_I2CMPHY_STAT0,
+ HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
+ dwhdmi_write(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff));
+ dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff));
+ dwhdmi_write(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ dwhdmi_phy_wait_i2c_done(sc, 1000);
+}
+
+static void
+dwhdmi_phy_enable_power(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_PDZ_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_enable_tmds(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_gen2_pddq(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_sel_interface_control(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static void
+dwhdmi_phy_enable_svsret(struct dwhdmi_softc *sc, uint8_t enable)
+{
+ uint8_t reg;
+
+ reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
+ reg &= ~HDMI_PHY_CONF0_SVSRET_MASK;
+ reg |= (enable << HDMI_PHY_CONF0_SVSRET_OFFSET);
+ dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
+}
+
+static inline void
+dwhdmi_phy_test_clear(struct dwhdmi_softc *sc, unsigned char bit)
+{
+ uint8_t val;
+
+ val = dwhdmi_read(sc, HDMI_PHY_TST0);
+ val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
+ val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
+ HDMI_PHY_TST0_TSTCLR_MASK;
+ dwhdmi_write(sc, HDMI_PHY_TST0, val);
+}
+
+static int
+dwhdmi_phy_configure(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
+{
+ const struct dwhdmi_mpll_config *mpll_conf;
+ const struct dwhdmi_phy_config *phy_conf;
+ uint8_t val;
+ uint8_t msec;
+
+ dwhdmi_write(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS);
+
+ /* gen2 tx power off */
+ dwhdmi_phy_gen2_txpwron(sc, 0);
+
+ /* gen2 pddq */
+ dwhdmi_phy_gen2_pddq(sc, 1);
+
+ /* PHY reset */
+ dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
+ dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT);
+
+ dwhdmi_write(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT);
+
+ dwhdmi_phy_test_clear(sc, 1);
+ dwhdmi_write(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ dwhdmi_phy_test_clear(sc, 0);
+
+ /*
+ * Following initialization are for 8bit per color case
+ */
+
+ /*
+ * PLL/MPLL config
+ */
+ for (mpll_conf = &sc->sc_mpll_config[0]; mpll_conf->pixel_clock != 0; mpll_conf++)
+ if (mode->clock <= mpll_conf->pixel_clock)
+ break;
+
+ dwhdmi_phy_i2c_write(sc, mpll_conf->cpce, HDMI_PHY_I2C_CPCE_CTRL);
+ dwhdmi_phy_i2c_write(sc, mpll_conf->gmp, HDMI_PHY_I2C_GMPCTRL);
+ dwhdmi_phy_i2c_write(sc, mpll_conf->curr, HDMI_PHY_I2C_CURRCTRL);
+
+ for (phy_conf = &sc->sc_phy_config[0]; phy_conf->pixel_clock != 0; phy_conf++)
+ if (mode->clock <= phy_conf->pixel_clock)
+ break;
+
+ dwhdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL);
+ dwhdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL);
+
+ dwhdmi_phy_i2c_write(sc, phy_conf->term, HDMI_PHY_I2C_TXTERM);
+ dwhdmi_phy_i2c_write(sc, phy_conf->sym, HDMI_PHY_I2C_CKSYMTXCTRL);
+ dwhdmi_phy_i2c_write(sc, phy_conf->vlev, HDMI_PHY_I2C_VLEVCTRL);
+
+ /* REMOVE CLK TERM */
+ dwhdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL);
+
+ dwhdmi_phy_enable_power(sc, 1);
+
+ /* toggle TMDS enable */
+ dwhdmi_phy_enable_tmds(sc, 0);
+ dwhdmi_phy_enable_tmds(sc, 1);
+
+ /* gen2 tx power on */
+ dwhdmi_phy_gen2_txpwron(sc, 1);
+ dwhdmi_phy_gen2_pddq(sc, 0);
+
+ switch (sc->sc_phytype) {
+ case 0xb2: /* MHL PHY HEAC */
+ case 0xc2: /* MHL PHY */
+ case 0xf3: /* HDMI 2.0 TX PHY */
+ dwhdmi_phy_enable_svsret(sc, 1);
+ break;
+ }
+
+ /*Wait for PHY PLL lock */
+ msec = 4;
+ val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+ while (val == 0) {
+ delay(1000);
+ if (msec-- == 0) {
+ device_printf(sc->sc_dev, "PHY PLL not locked\n");
+ return (-1);
+ }
+ val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+ }
+
+ return (0);
+}
+
+static void
+dwhdmi_phy_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
+{
+ int i;
+
+ /* HDMI Phy spec says to do the phy initialization sequence twice */
+ for (i = 0 ; i < 2 ; i++) {
+ dwhdmi_phy_sel_data_en_pol(sc, 1);
+ dwhdmi_phy_sel_interface_control(sc, 0);
+ dwhdmi_phy_enable_tmds(sc, 0);
+ dwhdmi_phy_enable_power(sc, 0);
+
+ /* Enable CSC */
+ dwhdmi_phy_configure(sc, mode);
+ }
+}
+
+enum drm_connector_status
+dwhdmi_phy_detect(struct dwhdmi_softc *sc, bool force)
+{
+ uint8_t val;
+
+ val = dwhdmi_read(sc, HDMI_PHY_STAT0);
+
+ return ((val & HDMI_PHY_STAT0_HPD) != 0) ?
+ connector_status_connected :
+ connector_status_disconnected;
+}
+
+void
+dwhdmi_phy_enable(struct dwhdmi_softc *sc)
+{
+}
+
+void
+dwhdmi_phy_disable(struct dwhdmi_softc *sc)
+{
+}
+
+void
+dwhdmi_phy_mode_set(struct dwhdmi_softc *sc,
+ struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+ dwhdmi_phy_init(sc, adjusted_mode);
+}