summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authormartin <martin@NetBSD.org>2019-11-16 16:48:25 +0000
committermartin <martin@NetBSD.org>2019-11-16 16:48:25 +0000
commitbbfa294c6ccd6a3fa77a3807ef036dbddf5efd73 (patch)
tree83c2b7cbd8258c08323e34a382dc62a2391a43b5 /sys/dev
parentc26e2b221139ff5820e79cd75d8ca613432dcb66 (diff)
Pull up following revision(s) (requested by jmcneill in ticket #427):
sys/dev/ic/dw_hdmi_phy.c: revision 1.2 sys/dev/ic/dw_hdmi.c: revision 1.4 sys/dev/fdt/ausoc.c: revision 1.5 sys/dev/ic/dw_hdmi.h: revision 1.2 sys/dev/ic/dw_hdmi.h: revision 1.3 sys/dev/ic/dw_hdmi.h: revision 1.4 sys/conf/files: revision 1.1242 sys/dev/fdt/fdtvar.h: revision 1.57 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.11 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.12 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.13 sys/arch/evbarm/conf/GENERIC64: revision 1.110 sys/arch/arm/rockchip/rk_drm.c: revision 1.1 sys/arch/arm/rockchip/rk_drm.c: revision 1.2 sys/arch/evbarm/conf/GENERIC64: revision 1.112 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.1 sys/dev/fdt/fdt_clock.c: revision 1.10 sys/arch/evbarm/conf/GENERIC64: revision 1.113 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.2 sys/arch/arm/rockchip/rk_drm.h: revision 1.1 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.3 sys/arch/arm/rockchip/rk_fb.c: revision 1.1 sys/arch/arm/dts/rk3399-rockpro64.dts: revision 1.9 sys/arch/arm/rockchip/rk_vop.c: revision 1.1 sys/arch/arm/rockchip/rk_vop.c: revision 1.2 sys/arch/arm/rockchip/rk_i2c.c: revision 1.6 sys/arch/arm/rockchip/rk_cru.h: revision 1.6 sys/arch/arm/rockchip/rk_cru.h: revision 1.7 sys/arch/arm/rockchip/rk_cru_composite.c: revision 1.4 sys/arch/arm/rockchip/rk_cru_composite.c: revision 1.5 sys/arch/arm/rockchip/files.rockchip: revision 1.21 sys/arch/arm/rockchip/rk_i2s.c: revision 1.1 sys/arch/arm/rockchip/files.rockchip: revision 1.22 sys/dev/ic/dw_hdmi.c: revision 1.2 sys/dev/ic/dw_hdmi_phy.c: revision 1.1 sys/dev/ic/dw_hdmi.c: revision 1.3 Support reads of more than 32 bytes in a single xfer. Add support for internal DesignWare HDMI PHYs Add fdtbus_clock_enable and fdtbus_clock_enable_index shortcuts Add HDMI and VOP clocks WIP display driver for Rockchip RK3399 Add (commented out) Rockchip display support Select the correct MPLL and PHY settings for the requested pixel clock Force DCLK_VOP0/1 dividers to 1 and select closest match when setting PLL rates. Fix typo in phy config table Fix a few swapped fields Remove debug output Enable Rockchip display support Set sysclk rate at set_format time, so the link set_format callback can read the new sysclk Add I2S audio input support. Add software volume controls. Add support for I2S clocks. Add driver for Rockchip I2S/PCM controller. Enable HDMI audio on ROCKPro64 Add rki2s Add audio support
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);
+}