diff --git a/bridges/anxdp/anx_dp.c b/bridges/anxdp/anx_dp.c new file mode 100644 index 0000000..bdc689f --- /dev/null +++ b/bridges/anxdp/anx_dp.c @@ -0,0 +1,1224 @@ +/* $NetBSD: anx_dp.c,v 1.5 2021/12/19 12:43:37 riastradh Exp $ */ + +/*- + * Copyright (c) 2022 Jesper Schmitz Mouridsen + * Copyright (c) 2019 Jonathan A. Kollasch + * 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 ``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 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. + */ + + +/* +from sys/cdefs.h from NetBSD + */ +/* Macros for min/max. */ +#define __MIN(a,b) ((/*CONSTCOND*/(a)<=(b))?(a):(b)) +#define __MAX(a,b) ((/*CONSTCOND*/(a)>(b))?(a):(b)) + +#define __BIT(__n) \ +(((uintmax_t)(__n) >= sizeof(long)*8 * sizeof(uintmax_t)) ? 0 : \ + ((uintmax_t)1 << (uintmax_t)((__n) & (sizeof(long)*8 * sizeof(uintmax_t) - 1)))) + +/* __BITS(m, n): bits m through n, m < n. */ +#define __BITS(__m, __n) \ + ((__BIT(__MAX((__m), (__n)) + 1) - 1) ^ (__BIT(__MIN((__m), (__n))) - 1)) + + +/* find least significant bit that is set */ +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) + +#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) +#define __SHIFTOUT_MASK(__mask) __SHIFTOUT((__mask), (__mask)) + +/* end off from sys/cdefs.h NetBSD */ + +#include +#include +#include +#include +#include +#include +#include + +#include "anx_dp.h" +#include "iicbus_if.h" +#if ANXDP_AUDIO +#include +#endif + +#include +#include +#include +#include +#include +#include +#define ANXDP_DP_TX_VERSION 0x010 +#define ANXDP_TX_SW_RESET 0x014 +#define RESET_DP_TX __BIT(0) +#define ANXDP_FUNC_EN_1 0x018 +#define MASTER_VID_FUNC_EN_N __BIT(7) +#define RK_VID_CAP_FUNC_EN_N __BIT(6) +#define SLAVE_VID_FUNC_EN_N __BIT(5) +#define RK_VID_FIFO_FUNC_EN_N __BIT(5) +#define AUD_FIFO_FUNC_EN_N __BIT(4) +#define AUD_FUNC_EN_N __BIT(3) +#define HDCP_FUNC_EN_N __BIT(2) +#define CRC_FUNC_EN_N __BIT(1) +#define SW_FUNC_EN_N __BIT(0) +#define ANXDP_FUNC_EN_2 0x01c +#define SSC_FUNC_EN_N __BIT(7) +#define AUX_FUNC_EN_N __BIT(2) +#define SERDES_FIFO_FUNC_EN_N __BIT(1) +#define LS_CLK_DOMAIN_FUNC_EN_N __BIT(0) +#define ANXDP_VIDEO_CTL_1 0x020 +#define VIDEO_EN __BIT(7) +#define VIDEO_MUTE __BIT(6) +#define ANXDP_VIDEO_CTL_2 0x024 +#define ANXDP_VIDEO_CTL_3 0x028 +#define ANXDP_VIDEO_CTL_4 0x02c +#define ANXDP_VIDEO_CTL_8 0x03c +#define ANXDP_VIDEO_CTL_10 0x044 +#define F_SEL __BIT(4) +#define SLAVE_I_SCAN_CFG __BIT(2) +#define SLAVE_VSYNC_P_CFG __BIT(1) +#define SLAVE_HSYNC_P_CFG __BIT(0) +#define ANXDP_PLL_REG_1 0x0fc +#define REF_CLK_24M __BIT(0) +#define RKANXDP_PD 0x12c +#define DP_INC_BG __BIT(7) +#define DP_EXP_PD __BIT(6) +#define DP_PHY_PD __BIT(5) +#define RK_AUX_PD __BIT(5) +#define AUX_PD __BIT(4) +#define RK_PLL_PD __BIT(4) +#define CHx_PD(x) __BIT(x) /* 0<=x<=3 */ +#define DP_ALL_PD __BITS(7,0) +#define ANXDP_LANE_MAP 0x35c +#define ANXDP_ANALOG_CTL_1 0x370 +#define TX_TERMINAL_CTRL_50_OHM __BIT(4) +#define ANXDP_ANALOG_CTL_2 0x374 +#define SEL_24M __BIT(3) +#define TX_DVDD_BIT_1_0625V 0x4 +#define ANXDP_ANALOG_CTL_3 0x378 +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) +#define ANXDP_PLL_FILTER_CTL_1 0x37c +#define PD_RING_OSC __BIT(6) +#define AUX_TERMINAL_CTRL_50_OHM (2 << 4) +#define TX_CUR1_2X __BIT(2) +#define TX_CUR_16_MA 3 +#define ANXDP_TX_AMP_TUNING_CTL 0x380 +#define ANXDP_AUX_HW_RETRY_CTL 0x390 +#define AUX_BIT_PERIOD_EXPECTED_DELAY(x) __SHIFTIN((x), __BITS(10,8)) +#define AUX_HW_RETRY_INTERVAL_600_US __SHIFTIN(0, __BITS(4,3)) +#define AUX_HW_RETRY_INTERVAL_800_US __SHIFTIN(1, __BITS(4,3)) +#define AUX_HW_RETRY_INTERVAL_1000_US __SHIFTIN(2, __BITS(4,3)) +#define AUX_HW_RETRY_INTERVAL_1800_US __SHIFTIN(3, __BITS(4,3)) +#define AUX_HW_RETRY_COUNT_SEL(x) __SHIFTIN((x), __BITS(2,0)) +#define ANXDP_COMMON_INT_STA_1 0x3c4 +#define PLL_LOCK_CHG __BIT(6) +#define ANXDP_COMMON_INT_STA_2 0x3c8 +#define ANXDP_COMMON_INT_STA_3 0x3cc +#define ANXDP_COMMON_INT_STA_4 0x3d0 +#define ANXDP_DP_INT_STA 0x3dc +#define INT_HPD __BIT(6) +#define HW_TRAINING_FINISH __BIT(5) +#define RPLY_RECEIV __BIT(1) +#define AUX_ERR __BIT(0) +#define ANXDP_SYS_CTL_1 0x600 +#define DET_STA __BIT(2) +#define FORCE_DET __BIT(1) +#define DET_CTRL __BIT(0) +#define ANXDP_SYS_CTL_2 0x604 +#define ANXDP_SYS_CTL_3 0x608 +#define HPD_STATUS __BIT(6) +#define F_HPD __BIT(5) +#define HPD_CTRL __BIT(4) +#define HDCP_RDY __BIT(3) +#define STRM_VALID __BIT(2) +#define F_VALID __BIT(1) +#define VALID_CTRL __BIT(0) +#define ANXDP_SYS_CTL_4 0x60c +#define ANXDP_PKT_SEND_CTL 0x640 +#define ANXDP_HDCP_CTL 0x648 +#define ANXDP_LINK_BW_SET 0x680 +#define ANXDP_LANE_COUNT_SET 0x684 +#define ANXDP_TRAINING_PTN_SET 0x688 +#define SCRAMBLING_DISABLE __BIT(5) +#define SW_TRAINING_PATTERN_SET_PTN2 __SHIFTIN(2, __BITS(1,0)) +#define SW_TRAINING_PATTERN_SET_PTN1 __SHIFTIN(1, __BITS(1,0)) +#define ANXDP_LNx_LINK_TRAINING_CTL(x) (0x68c + 4 * (x)) /* 0 <= x <= 3 */ +#define MAX_PRE_REACH __BIT(5) +#define PRE_EMPHASIS_SET(x) __SHIFTIN((x), __BITS(4,3)) +#define MAX_DRIVE_REACH __BIT(2) +#define DRIVE_CURRENT_SET(x) __SHIFTIN((x), __BITS(1,0)) +#define ANXDP_DEBUG_CTL 0x6c0 +#define PLL_LOCK __BIT(4) +#define F_PLL_LOCK __BIT(3) +#define PLL_LOCK_CTRL __BIT(2) +#define PN_INV __BIT(0) +#define ANXDP_LINK_DEBUG_CTL 0x6e0 +#define ANXDP_PLL_CTL 0x71c +#define ANXDP_PHY_PD 0x720 +#define ANXDP_PHY_TEST 0x724 +#define MACRO_RST __BIT(5) +#define ANXDP_M_AUD_GEN_FILTER_TH 0x778 +#define ANXDP_AUX_CH_STA 0x780 +#define AUX_BUSY __BIT(4) +#define AUX_STATUS(x) __SHIFTOUT((x), __BITS(3,0)) +#define ANXDP_AUX_ERR_NUM 0x784 +#define ANXDP_AUX_CH_DEFER_CTL 0x788 +#define DEFER_CTRL_EN __BIT(7) +#define DEFER_COUNT(x) __SHIFTIN((x), __BITS(6,0)) +#define ANXDP_AUX_RX_COMM 0x78c +#define AUX_RX_COMM_I2C_DEFER __BIT(3) +#define AUX_RX_COMM_AUX_DEFER __BIT(1) +#define ANXDP_BUFFER_DATA_CTL 0x790 +#define BUF_CLR __BIT(7) +#define BUF_DATA_COUNT(x) __SHIFTIN((x), __BITS(4,0)) +#define ANXDP_AUX_CH_CTL_1 0x794 +#define AUX_LENGTH(x) __SHIFTIN((x) - 1, __BITS(7,4)) +#define AUX_TX_COMM(x) __SHIFTOUT(x, __BITS(3,0)) +#define AUX_TX_COMM_DP __BIT(3) +#define AUX_TX_COMM_MOT __BIT(2) +#define AUX_TX_COMM_READ __BIT(0) +#define ANXDP_AUX_ADDR_7_0 0x798 +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) +#define ANXDP_AUX_ADDR_15_8 0x79c +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) +#define ANXDP_AUX_ADDR_19_16 0x7a0 +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0xf) +#define ANXDP_AUX_CH_CTL_2 0x7a4 +#define ADDR_ONLY __BIT(1) +#define AUX_EN __BIT(0) +#define ANXDP_BUF_DATA(x) (0x7c0 + 4 * (x)) +#define ANXDP_SOC_GENERAL_CTL 0x800 +#define AUDIO_MODE_SPDIF_MODE __BIT(8) +#define VIDEO_MODE_SLAVE_MODE __BIT(1) +#define ANXDP_CRC_CON 0x890 +#define ANXDP_PLL_REG_2 0x9e4 +#define ANXDP_PLL_REG_3 0x9e8 +#define ANXDP_PLL_REG_4 0x9ec +#define ANXDP_PLL_REG_5 0xa00 + + +#define ANXDP_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define ANXDP_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) + +#define to_anxdp_connector(x) container_of(x, struct anxdp_connector, base) + + +struct anxdp_link { + uint8_t revision; + u_int rate; + u_int num_lanes; + bool enhanced_framing; +}; + +#if ANXDP_AUDIO +enum anxdp_dai_mixer_ctrl { + ANXDP_DAI_OUTPUT_CLASS, + ANXDP_DAI_INPUT_CLASS, + + ANXDP_DAI_OUTPUT_MASTER_VOLUME, + ANXDP_DAI_INPUT_DAC_VOLUME, + + ANXDP_DAI_MIXER_CTRL_LAST +}; + +static void +anxdp_audio_init(struct anxdp_softc *sc) +{ +} +#endif + +static inline const bool +isrockchip(struct anxdp_softc * const sc) +{ + return (sc->sc_flags & ANXDP_FLAG_ROCKCHIP) != 0; +} + +static enum drm_connector_status +anxdp_connector_detect(struct drm_connector *connector, bool force) +{ +#if 0 + struct anxdp_connector *anxdp_connector = to_anxdp_connector(connector); + struct anxdp_softc * const sc = anxdp_connector->sc; + + /* XXX HPD */ +#endif + return connector_status_connected; +} + +static void +anxdp_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs anxdp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = anxdp_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = anxdp_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static void +anxdp_analog_power_up_all(struct anxdp_softc * const sc) +{ + const bus_size_t pd_reg = isrockchip(sc) ? RKANXDP_PD : ANXDP_PHY_PD; + + ANXDP_WRITE(sc, pd_reg, DP_ALL_PD); + DELAY(15); + ANXDP_WRITE(sc, pd_reg, + DP_ALL_PD & ~DP_INC_BG); + DELAY(15); + ANXDP_WRITE(sc, pd_reg, 0); +} + +static int +anxdp_await_pll_lock(struct anxdp_softc * const sc) +{ + u_int timeout; + + for (timeout = 0; timeout < 100; timeout++) { + if ((ANXDP_READ(sc, ANXDP_DEBUG_CTL) & PLL_LOCK) != 0) + return 0; + DELAY(20); + } + + return ETIMEDOUT; +} + +static void +anxdp_init_hpd(struct anxdp_softc * const sc) +{ + uint32_t sc3; + + ANXDP_WRITE(sc, ANXDP_COMMON_INT_STA_4, 0x7); + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, INT_HPD); + + sc3 = ANXDP_READ(sc, ANXDP_SYS_CTL_3); + sc3 &= ~(F_HPD | HPD_CTRL); + ANXDP_WRITE(sc, ANXDP_SYS_CTL_3, sc3); + + sc3 = ANXDP_READ(sc, ANXDP_SYS_CTL_3); + sc3 |= F_HPD | HPD_CTRL; + ANXDP_WRITE(sc, ANXDP_SYS_CTL_3, sc3); +} + +static void +anxdp_init_aux(struct anxdp_softc * const sc) +{ + uint32_t fe2, pd, hrc; + const bus_size_t pd_reg = isrockchip(sc) ? RKANXDP_PD : ANXDP_PHY_PD; + const uint32_t pd_mask = isrockchip(sc) ? RK_AUX_PD : AUX_PD; + + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, + RPLY_RECEIV | AUX_ERR); + + pd = ANXDP_READ(sc, pd_reg); + pd |= pd_mask; + ANXDP_WRITE(sc, pd_reg, pd); + + DELAY(11); + + pd = ANXDP_READ(sc, pd_reg); + pd &= ~pd_mask; + ANXDP_WRITE(sc, pd_reg, pd); + + fe2 = ANXDP_READ(sc, ANXDP_FUNC_EN_2); + fe2 |= AUX_FUNC_EN_N; + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, fe2); + + hrc = AUX_HW_RETRY_COUNT_SEL(0) | AUX_HW_RETRY_INTERVAL_600_US; + if (!isrockchip(sc)) + hrc |= AUX_BIT_PERIOD_EXPECTED_DELAY(3); + ANXDP_WRITE(sc, ANXDP_AUX_HW_RETRY_CTL, hrc); + + ANXDP_WRITE(sc, ANXDP_AUX_CH_DEFER_CTL, + DEFER_CTRL_EN | DEFER_COUNT(1)); + + fe2 = ANXDP_READ(sc, ANXDP_FUNC_EN_2); + fe2 &= ~AUX_FUNC_EN_N; + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, fe2); +} + +static int +anxdp_connector_get_modes(struct drm_connector *connector) +{ + struct anxdp_connector *anxdp_connector = to_anxdp_connector(connector); + struct anxdp_softc * const sc = anxdp_connector->sc; + struct edid *pedid = NULL; + int error; + + pedid = drm_get_edid(connector, sc->ddc); + +#if ANXDP_AUDIO + if (pedid) { + anxdp_connector->monitor_audio = + drm_detect_monitor_audio(pedid); + } else { + anxdp_connector->monitor_audio = false; + } + +#endif + drm_connector_update_edid_property(connector, pedid); + if (pedid == NULL) + return 0; + + error = drm_add_edid_modes(connector, pedid); + + if (pedid != NULL) + kfree(pedid); + + return error; +} + +static struct drm_encoder * +anxdp_connector_best_encoder(struct drm_connector *connector) +{ + struct anxdp_connector *anxdp_connector = to_anxdp_connector(connector); + + return anxdp_connector->encoder; +} + +static const struct drm_connector_helper_funcs anxdp_connector_helper_funcs = { + .get_modes = anxdp_connector_get_modes, + .best_encoder = anxdp_connector_best_encoder, +}; + +static int +anxdp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) +{ + struct anxdp_softc *sc = bridge->driver_private; + struct anxdp_connector *anxdp_connector = &sc->sc_connector; + struct drm_connector *connector = &anxdp_connector->base; + int error; + + anxdp_connector->sc = sc; + + connector->polled = + DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_connector_init(bridge->dev, connector, &anxdp_connector_funcs, + connector->connector_type); + drm_connector_helper_add(connector, &anxdp_connector_helper_funcs); + + error = drm_connector_attach_encoder(connector, bridge->encoder); + if (error) + return error; + + return drm_connector_register(connector); + +} + +static void +anxdp_macro_reset(struct anxdp_softc * const sc) +{ + uint32_t val; + + val = ANXDP_READ(sc, ANXDP_PHY_TEST); + val |= MACRO_RST; + ANXDP_WRITE(sc, ANXDP_PHY_TEST, val); + DELAY(10); + val &= ~MACRO_RST; + ANXDP_WRITE(sc, ANXDP_PHY_TEST, val); +} + +static void +anxdp_link_start(struct anxdp_softc * const sc, struct anxdp_link * const link) +{ + uint8_t training[4]; + uint8_t bw[2]; + uint32_t val; + int ret; + + ANXDP_WRITE(sc, ANXDP_LINK_BW_SET, + drm_dp_link_rate_to_bw_code(link->rate)); + ANXDP_WRITE(sc, ANXDP_LANE_COUNT_SET, + link->num_lanes); + + bw[0] = drm_dp_link_rate_to_bw_code(link->rate); + bw[1] = link->num_lanes; + if (link->enhanced_framing) + bw[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + ret = drm_dp_dpcd_write(&sc->sc_dpaux, DP_LINK_BW_SET, bw, sizeof(bw)); + if (ret < 0) + return; + + for (u_int i = 0; i < link->num_lanes; i++) { + val = ANXDP_READ(sc, + ANXDP_LNx_LINK_TRAINING_CTL(i)); + val &= ~(PRE_EMPHASIS_SET(3)|DRIVE_CURRENT_SET(3)); + val |= PRE_EMPHASIS_SET(0); + ANXDP_WRITE(sc, + ANXDP_LNx_LINK_TRAINING_CTL(i), val); + } + + if (anxdp_await_pll_lock(sc) != 0) { + device_printf(sc->sc_dev, "PLL lock timeout\n"); + } + + for (u_int i = 0; i < link->num_lanes; i++) { + training[i] = DP_TRAIN_PRE_EMPH_LEVEL_0 | + DP_TRAIN_VOLTAGE_SWING_LEVEL_0; + } + + drm_dp_dpcd_write(&sc->sc_dpaux, DP_TRAINING_LANE0_SET, training, + link->num_lanes); +} + +static void +anxdp_process_clock_recovery(struct anxdp_softc * const sc, + struct anxdp_link * const link) +{ + u_int i, tries; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + uint8_t training[4]; + + ANXDP_WRITE(sc, ANXDP_TRAINING_PTN_SET, + SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1); + drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_TRAINING_PATTERN_SET, + DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1); + + tries = 0; +again: + if (tries++ >= 10) { + device_printf(sc->sc_dev, "cr fail\n"); + return; + } + + drm_dp_link_train_clock_recovery_delay(sc->sc_dpcd); + if (DP_LINK_STATUS_SIZE != + drm_dp_dpcd_read_link_status(&sc->sc_dpaux, link_status)) { + return; + } + if (!drm_dp_clock_recovery_ok(link_status, link->num_lanes)) { + goto cr_fail; + } + + wakeup(sc->sc_dev); + return; + +cr_fail: + for (i = 0; i < link->num_lanes; i++) { + uint8_t vs, pe; + vs = drm_dp_get_adjust_request_voltage(link_status, i); + pe = drm_dp_get_adjust_request_pre_emphasis(link_status, i); + training[i] = vs | pe; + } + + for (i = 0; i < link->num_lanes; i++) { + ANXDP_WRITE(sc, + ANXDP_LNx_LINK_TRAINING_CTL(i), training[i]); + } + drm_dp_dpcd_write(&sc->sc_dpaux, DP_TRAINING_LANE0_SET, training, + link->num_lanes); + goto again; +} + +static void +anxdp_process_eq(struct anxdp_softc * const sc, struct anxdp_link * const link) +{ + u_int i, tries; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + uint8_t training[4]; + + ANXDP_WRITE(sc, ANXDP_TRAINING_PTN_SET, + SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2); + drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_TRAINING_PATTERN_SET, + DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2); + + tries = 0; +again: + if (tries++ >= 10) { + device_printf(sc->sc_dev, "eq fail\n"); + return; + } + drm_dp_link_train_channel_eq_delay(sc->sc_dpcd); + if (DP_LINK_STATUS_SIZE != + drm_dp_dpcd_read_link_status(&sc->sc_dpaux, link_status)) { + return; + } + if (!drm_dp_channel_eq_ok(link_status, link->num_lanes)) { + goto eq_fail; + } + + return; + +eq_fail: + for (i = 0; i < link->num_lanes; i++) { + uint8_t vs, pe; + vs = drm_dp_get_adjust_request_voltage(link_status, i); + pe = drm_dp_get_adjust_request_pre_emphasis(link_status, i); + training[i] = vs | pe; + } + for (i = 0; i < link->num_lanes; i++) { + ANXDP_WRITE(sc, + ANXDP_LNx_LINK_TRAINING_CTL(i), training[i]); + } + drm_dp_dpcd_write(&sc->sc_dpaux, DP_TRAINING_LANE0_SET, training, + link->num_lanes); + goto again; +} + +static void +anxdp_train_link(struct anxdp_softc * const sc) +{ + struct anxdp_link link; + uint8_t values[3], power; + int ret; + + anxdp_macro_reset(sc); + + ret = drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, values, + sizeof(values)); + if (ret < 0) { + device_printf(sc->sc_dev, "link probe failed\n"); + return; + } + memset(&link, 0, sizeof(link)); + link.revision = values[0]; + link.rate = drm_dp_bw_code_to_link_rate(values[1]); + link.num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; + if (values[2] & DP_ENHANCED_FRAME_CAP) + link.enhanced_framing = true; + + if (link.revision >= 0x11) { + if (drm_dp_dpcd_readb(&sc->sc_dpaux, DP_SET_POWER, &power) < 0) + return; + power &= ~DP_SET_POWER_MASK; + power |= DP_SET_POWER_D0; + if (drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_SET_POWER, power) < 0) + return; + DELAY(2000); + } + + if (DP_RECEIVER_CAP_SIZE != drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, + sc->sc_dpcd, DP_RECEIVER_CAP_SIZE)) + return; + + anxdp_link_start(sc, &link); + anxdp_process_clock_recovery(sc, &link); + anxdp_process_eq(sc, &link); + + ANXDP_WRITE(sc, ANXDP_TRAINING_PTN_SET, 0); + drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + +} + +static void +anxdp_bringup(struct anxdp_softc * const sc) +{ + uint32_t val; + val = ANXDP_READ(sc, ANXDP_VIDEO_CTL_1); + val &= ~VIDEO_EN; + ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + + val = ANXDP_READ(sc, ANXDP_VIDEO_CTL_1); + val &= ~VIDEO_MUTE; + ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + + val = SW_FUNC_EN_N; + if (isrockchip(sc)) { + val |= RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N; + } else { + val |= MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | HDCP_FUNC_EN_N; + } + ANXDP_WRITE(sc, ANXDP_FUNC_EN_1, val); + + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, + SSC_FUNC_EN_N | AUX_FUNC_EN_N | SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N); + + DELAY(30); + + ANXDP_WRITE(sc, ANXDP_M_AUD_GEN_FILTER_TH, 2); + ANXDP_WRITE(sc, ANXDP_SOC_GENERAL_CTL, 0x101); + + ANXDP_WRITE(sc, ANXDP_TX_SW_RESET, + RESET_DP_TX); + + ANXDP_WRITE(sc, ANXDP_ANALOG_CTL_1, + TX_TERMINAL_CTRL_50_OHM); + ANXDP_WRITE(sc, ANXDP_ANALOG_CTL_2, + SEL_24M | TX_DVDD_BIT_1_0625V); + if (isrockchip(sc)) { + ANXDP_WRITE(sc, ANXDP_PLL_REG_1, + REF_CLK_24M); + ANXDP_WRITE(sc, ANXDP_PLL_REG_2, + 0x95); + ANXDP_WRITE(sc, ANXDP_PLL_REG_3, + 0x40); + ANXDP_WRITE(sc, ANXDP_PLL_REG_4, + 0x58); + ANXDP_WRITE(sc, ANXDP_PLL_REG_5, + 0x22); + } + ANXDP_WRITE(sc, ANXDP_ANALOG_CTL_3, + DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO); + ANXDP_WRITE(sc, ANXDP_PLL_FILTER_CTL_1, + PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM | TX_CUR1_2X | TX_CUR_16_MA); + ANXDP_WRITE(sc, ANXDP_TX_AMP_TUNING_CTL, 0); + + val = ANXDP_READ(sc, ANXDP_FUNC_EN_1); + val &= ~SW_FUNC_EN_N; + ANXDP_WRITE(sc, ANXDP_FUNC_EN_1, val); + + anxdp_analog_power_up_all(sc); + + ANXDP_WRITE(sc, ANXDP_COMMON_INT_STA_1, + PLL_LOCK_CHG); + + val = ANXDP_READ(sc, ANXDP_DEBUG_CTL); + val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + ANXDP_WRITE(sc, ANXDP_DEBUG_CTL, val); + + if (anxdp_await_pll_lock(sc) != 0) { + device_printf(sc->sc_dev, "PLL lock timeout\n"); + } + + val = ANXDP_READ(sc, ANXDP_FUNC_EN_2); + val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N | + AUX_FUNC_EN_N); + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, val); + + anxdp_init_hpd(sc); + anxdp_init_aux(sc); +} + + + +static void +anxdp_bridge_enable(struct drm_bridge *bridge) +{ + + struct anxdp_softc * const sc = bridge->driver_private; + uint32_t val; + + val = ANXDP_READ(sc, ANXDP_FUNC_EN_1); + if (isrockchip(sc)) { + val &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N); + } else { + val &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N); + val |= MASTER_VID_FUNC_EN_N; + } + ANXDP_WRITE(sc, ANXDP_FUNC_EN_1, val); + + val = ANXDP_READ(sc, ANXDP_VIDEO_CTL_10); + val &= ~(SLAVE_I_SCAN_CFG|SLAVE_VSYNC_P_CFG|SLAVE_HSYNC_P_CFG); + if ((sc->sc_curmode.flags & DRM_MODE_FLAG_INTERLACE) != 0) + val |= SLAVE_I_SCAN_CFG; + if ((sc->sc_curmode.flags & DRM_MODE_FLAG_NVSYNC) != 0) + val |= SLAVE_VSYNC_P_CFG; + if ((sc->sc_curmode.flags & DRM_MODE_FLAG_NHSYNC) != 0) + val |= SLAVE_HSYNC_P_CFG; + ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_10, val); + + ANXDP_WRITE(sc, ANXDP_SOC_GENERAL_CTL, + AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE); + + anxdp_train_link(sc); + + val = ANXDP_READ(sc, ANXDP_VIDEO_CTL_1); + val |= VIDEO_EN; + ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + + if (sc->sc_panel != NULL && + sc->sc_panel->funcs != NULL && + sc->sc_panel->funcs->enable != NULL) + sc->sc_panel->funcs->enable(sc->sc_panel); +#if ANXDP_AUDIO + + if (sc->sc_connector.monitor_audio) + anxdp_audio_init(sc); +#endif +} + +static void +anxdp_bridge_pre_enable(struct drm_bridge *bridge) +{ +} + +static void +anxdp_bridge_disable(struct drm_bridge *bridge) +{ +} + +static void +anxdp_bridge_post_disable(struct drm_bridge *bridge) +{ +} + +static void +anxdp_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct anxdp_softc * const sc = bridge->driver_private; + + sc->sc_curmode = *adjusted_mode; +} + +static bool +anxdp_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static const struct drm_bridge_funcs anxdp_bridge_funcs = { + .attach = anxdp_bridge_attach, + .enable = anxdp_bridge_enable, + .pre_enable = anxdp_bridge_pre_enable, + .disable = anxdp_bridge_disable, + .post_disable = anxdp_bridge_post_disable, + .mode_set = anxdp_bridge_mode_set, + .mode_fixup = anxdp_bridge_mode_fixup, +}; + +#if ANXDP_AUDIO +static int +anxdp_dai_set_format(audio_dai_tag_t dai, u_int format) +{ + return 0; +} + +static int +anxdp_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) +{ + /* Not supported */ + return 0; +} + +static void +anxdp_audio_swvol_codec(audio_filter_arg_t *arg) +{ + struct anxdp_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 +anxdp_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 anxdp_softc * const sc = priv; + + pfil->codec = anxdp_audio_swvol_codec; + pfil->context = sc; + + return 0; +} + +static int +anxdp_audio_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct anxdp_softc * const sc = priv; + + switch (mc->dev) { + case ANXDP_DAI_OUTPUT_MASTER_VOLUME: + case ANXDP_DAI_INPUT_DAC_VOLUME: + sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + return 0; + default: + return ENXIO; + } +} + +static int +anxdp_audio_get_port(void *priv, mixer_ctrl_t *mc) +{ + struct anxdp_softc * const sc = priv; + + switch (mc->dev) { + case ANXDP_DAI_OUTPUT_MASTER_VOLUME: + case ANXDP_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 +anxdp_audio_query_devinfo(void *priv, mixer_devinfo_t *di) +{ + switch (di->index) { + case ANXDP_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 ANXDP_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 ANXDP_DAI_OUTPUT_MASTER_VOLUME: + di->mixer_class = ANXDP_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 ANXDP_DAI_INPUT_DAC_VOLUME: + di->mixer_class = ANXDP_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 anxdp_dai_hw_if = { + .set_format = anxdp_audio_set_format, + .set_port = anxdp_audio_set_port, + .get_port = anxdp_audio_get_port, + .query_devinfo = anxdp_audio_query_devinfo, +}; +#endif + +static ssize_t +anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) +{ + struct anxdp_softc * const sc = container_of(dpaux, struct anxdp_softc, + sc_dpaux); + size_t loop_timeout = 0; + uint32_t val; + size_t i; + ssize_t ret = 0; + + ANXDP_WRITE(sc, ANXDP_BUFFER_DATA_CTL, + BUF_CLR); + + val = AUX_LENGTH(dpmsg->size); + if ((dpmsg->request & DP_AUX_I2C_MOT) != 0) + val |= AUX_TX_COMM_MOT; + + switch (dpmsg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_I2C_WRITE: + break; + case DP_AUX_I2C_READ: + val |= AUX_TX_COMM_READ; + break; + case DP_AUX_NATIVE_WRITE: + val |= AUX_TX_COMM_DP; + break; + case DP_AUX_NATIVE_READ: + val |= AUX_TX_COMM_READ | AUX_TX_COMM_DP; + break; + } + + ANXDP_WRITE(sc, ANXDP_AUX_CH_CTL_1, val); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_7_0, + AUX_ADDR_7_0(dpmsg->address)); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_15_8, + AUX_ADDR_15_8(dpmsg->address)); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_19_16, + AUX_ADDR_19_16(dpmsg->address)); + + if (!(dpmsg->request & DP_AUX_I2C_READ)) { + for (i = 0; i < dpmsg->size; i++) { + ANXDP_WRITE(sc, ANXDP_BUF_DATA(i), + ((const uint8_t *)(dpmsg->buffer))[i]); + ret++; + } + } + + + ANXDP_WRITE(sc, ANXDP_AUX_CH_CTL_2, + AUX_EN | ((dpmsg->size == 0) ? ADDR_ONLY : 0)); + + loop_timeout = 0; + val = ANXDP_READ(sc, ANXDP_AUX_CH_CTL_2); + while ((val & AUX_EN) != 0) { + if (++loop_timeout > 20000) { + ret = -ETIMEDOUT; + goto out; + } + DELAY(25); + val = ANXDP_READ(sc, + ANXDP_AUX_CH_CTL_2); + } + + loop_timeout = 0; + val = ANXDP_READ(sc, ANXDP_DP_INT_STA); + while (!(val & RPLY_RECEIV)) { + if (++loop_timeout > 2000) { + ret = -ETIMEDOUT; + goto out; + } + DELAY(10); + val = ANXDP_READ(sc, + ANXDP_DP_INT_STA); + } + + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, + RPLY_RECEIV); + + val = ANXDP_READ(sc, ANXDP_DP_INT_STA); + if ((val & AUX_ERR) != 0) { + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, + AUX_ERR); + ret = -EREMOTEIO; + goto out; + } + + val = ANXDP_READ(sc, ANXDP_AUX_CH_STA); + if (AUX_STATUS(val) != 0) { + ret = -EREMOTEIO; + goto out; + } + + if ((dpmsg->request & DP_AUX_I2C_READ)) { + for (i = 0; i < dpmsg->size; i++) { + val = ANXDP_READ(sc, + ANXDP_BUF_DATA(i)); + ((uint8_t *)(dpmsg->buffer))[i] = val & 0xffU; + ret++; + } + } + + val = ANXDP_READ(sc, ANXDP_AUX_RX_COMM); + if (val == AUX_RX_COMM_AUX_DEFER) + dpmsg->reply = DP_AUX_NATIVE_REPLY_DEFER; + else if (val == AUX_RX_COMM_I2C_DEFER) + dpmsg->reply = DP_AUX_I2C_REPLY_DEFER; + else if ((dpmsg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE || + (dpmsg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_READ) + dpmsg->reply = DP_AUX_I2C_REPLY_ACK; + else if ((dpmsg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE || + (dpmsg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ) + dpmsg->reply = DP_AUX_NATIVE_REPLY_ACK; + +out: + if (ret < 0) + anxdp_init_aux(sc); + + return ret; +} + +int +anxdp_attach(struct anxdp_softc *sc) +{ +#if ANXDP_AUDIO + sc->sc_swvol = 255; + + /* + * Initialize audio DAI + */ + sc->sc_dai.dai_set_format = anxdp_dai_set_format; + sc->sc_dai.dai_add_device = anxdp_dai_add_device; + sc->sc_dai.dai_hw_if = &anxdp_dai_hw_if; + sc->sc_dai.dai_dev = sc->sc_dev; + sc->sc_dai.dai_priv = sc; +#endif + + sc->sc_dpaux.name = "DP Aux"; + sc->sc_dpaux.transfer = anxdp_dp_aux_transfer; + sc->sc_dpaux.dev = sc->sc_dev; + if ((sc->iicbus = device_add_child(sc->sc_dev, "iicbus", -1)) == NULL) { + device_printf(sc->sc_dev, "could not allocate iicbus instance\n"); + return (ENXIO); + } + sc->ddc = i2c_bsd_adapter(sc->iicbus); + + if (drm_dp_aux_register(&sc->sc_dpaux) != 0) { + device_printf(sc->sc_dev, "registering DP Aux failed\n"); + } + + anxdp_bringup(sc); + + return 0; +} + +int +anxdp_bind(struct anxdp_softc *sc, struct drm_encoder *encoder) +{ + int error; + + sc->sc_connector.encoder = encoder; + + sc->sc_bridge.driver_private = sc; + sc->sc_bridge.funcs = &anxdp_bridge_funcs; + error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL,0); + if (error) + return EIO; + + if (sc->sc_panel != NULL && + sc->sc_panel->funcs != NULL && + sc->sc_panel->funcs->prepare != NULL) + sc->sc_panel->funcs->prepare(sc->sc_panel); + + return 0; +} + + + + + +static int anxdp_iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { + /* read buffer is max 16 bytes*/ + uint32_t val,loop_timeout,reg; + int i,j,k; + int ret=0; + struct anxdp_softc *sc = device_get_softc(dev); + + ANXDP_WRITE(sc, ANXDP_BUFFER_DATA_CTL, BUF_CLR); + for(i=0; i < nmsgs; i++) { + reg = AUX_LENGTH(msgs[i].len); + + if ((msgs[i].flags & IIC_M_RD)) + reg |= AUX_TX_COMM_READ; + + ANXDP_WRITE(sc, ANXDP_AUX_CH_CTL_1, reg); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_7_0, + AUX_ADDR_7_0(msgs[i].slave>>1)); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_15_8, + AUX_ADDR_15_8(msgs[i].slave>>1)); + ANXDP_WRITE(sc, ANXDP_AUX_ADDR_19_16, + AUX_ADDR_19_16(msgs[i].slave>>1)); + int iters = msgs[i].len / 16 == 0 ? 1 : msgs[i].len / 16; + + for (k = 0; k< iters; k++) { + + + if (!(msgs[i].flags & IIC_M_RD)) { + for (j = 0; j < msgs[i].len; j++) { + ANXDP_WRITE(sc, + ANXDP_BUF_DATA(j), + ((const uint8_t *)(msgs[i].buf))[j]); + ret++; + } + } + + + ANXDP_WRITE(sc, ANXDP_AUX_CH_CTL_2, + AUX_EN | ((msgs[i].len == 0) ? ADDR_ONLY : 0)); + + loop_timeout = 0; + val = ANXDP_READ(sc,ANXDP_AUX_CH_CTL_2); + while ((val & AUX_EN) != 0) { + if (++loop_timeout > 20000) { + ret = -ETIMEDOUT; + goto out; + } + DELAY(25); + val = ANXDP_READ(sc, + ANXDP_AUX_CH_CTL_2); + } + + loop_timeout = 0; + val = ANXDP_READ(sc,ANXDP_DP_INT_STA); + while (!(val & RPLY_RECEIV)) { + if (++loop_timeout > 2000) { + ret = -ETIMEDOUT; + goto out; + } + DELAY(10); + val = ANXDP_READ(sc, + ANXDP_DP_INT_STA); + } + + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, + RPLY_RECEIV); + + val = ANXDP_READ(sc,ANXDP_DP_INT_STA); + if ((val & AUX_ERR) != 0) { + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, + AUX_ERR); + ret = -EREMOTEIO; + goto out; + } + + val = ANXDP_READ(sc,ANXDP_AUX_CH_STA); + if (AUX_STATUS(val) != 0) { + ret = -EREMOTEIO; + goto out; + } + + if ((msgs[i].flags & IIC_M_RD)) { + int nbytes = msgs[i].len > 16 ? 16 : msgs[i].len; + for (j = 0; j < nbytes; j++) { + val = ANXDP_READ(sc, + ANXDP_BUF_DATA(j)); + msgs[i].buf[j+k*16] = val & 0xff; + } + + } + + val = ANXDP_READ(sc,ANXDP_AUX_RX_COMM); + } + } + + +out: + return 0; +} + + + + +static device_method_t +anxdp_methods[] = { + DEVMETHOD(iicbus_transfer,anxdp_iic_transfer), +}; +DEFINE_CLASS_0(anxdp, anxdp_driver,anxdp_methods,sizeof(struct anxdp_softc)); + diff --git a/bridges/anxdp/anx_dp.h b/bridges/anxdp/anx_dp.h new file mode 100644 index 0000000..351b253 --- /dev/null +++ b/bridges/anxdp/anx_dp.h @@ -0,0 +1,90 @@ + + +/* $NetBSD: anx_dp.h,v 1.5 2021/12/19 12:43:37 riastradh Exp $ */ + +/*- + * Copyright (c) 2019 Jonathan A. Kollasch + * 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 ``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 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. + */ + +#ifndef _DEV_IC_ANXDP_H +#include +__FBSDID("$FreeBSD$"); + +#define ANXDP_AUDIO 0 +#define _DEV_IC_ANXDP_H + +#if ANXDP_AUDIO +#include +#endif + +#include +#include +#include +#include +#include +#include + +struct anxdp_softc; + +struct anxdp_connector { + struct drm_connector base; + struct anxdp_softc *sc; + struct drm_encoder *encoder; +#if ANXDP_AUDIO + + bool monitor_audio; +#endif +}; + +struct anxdp_softc { + device_t sc_dev; + struct resource *res[2]; + u_int sc_flags; +#define ANXDP_FLAG_ROCKCHIP 1 + +#if ANXDP_AUDIO + struct audio_dai_device sc_dai; + uint8_t sc_swvol; + +#endif + struct anxdp_connector sc_connector; + struct drm_bridge sc_bridge; + struct drm_dp_aux sc_dpaux; + struct drm_panel *sc_panel; + uint8_t sc_dpcd[DP_RECEIVER_CAP_SIZE]; + device_t iicbus; + struct i2c_adapter *ddc; + struct drm_display_mode sc_curmode; + + +}; + + +int anxdp_attach(struct anxdp_softc *); +int anxdp_bind(struct anxdp_softc *, struct drm_encoder *); + +void anxdp_dpms(struct anxdp_softc *, int); +DECLARE_CLASS(anxdp_driver); +#endif /* !_DEV_IC_ANXDP_H */ diff --git a/core/drm_dp_helper.c b/core/drm_dp_helper.c index 19c99dd..756fba2 100644 --- a/core/drm_dp_helper.c +++ b/core/drm_dp_helper.c @@ -302,10 +302,11 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, if (ret != 1) goto out; } - - if (aux->is_remote) +#ifdef __linux__ + If (aux->is_remote) ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size); else +#endif ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, size); @@ -333,10 +334,11 @@ ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, void *buffer, size_t size) { int ret; - +#ifdef __linux if (aux->is_remote) ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size); else +#endif ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, size); @@ -597,7 +599,7 @@ EXPORT_SYMBOL(drm_dp_downstream_debug); /* * I2C-over-AUX implementation */ - +#ifdef __linux__ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | @@ -605,7 +607,7 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_10BIT_ADDR; } - +#endif static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg) { /* @@ -821,7 +823,9 @@ static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg, { msg->request = (i2c_msg->flags & I2C_M_RD) ? DP_AUX_I2C_READ : DP_AUX_I2C_WRITE; - if (!(i2c_msg->flags & I2C_M_STOP)) +#ifdef __linux__ + if (!(i2c_msg->flags & IIC_M_STOP)) +#endif msg->request |= DP_AUX_I2C_MOT; } @@ -862,11 +866,13 @@ static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES; module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644); MODULE_PARM_DESC(dp_aux_i2c_transfer_size, "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)"); - +#ifdef __linux__ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { + struct drm_dp_aux *aux = adapter->algo_data; + unsigned int i, j; unsigned transfer_size; struct drm_dp_aux_msg msg; @@ -937,7 +943,7 @@ static const struct i2c_algorithm drm_dp_i2c_algo = { .functionality = drm_dp_i2c_functionality, .master_xfer = drm_dp_i2c_xfer, }; - +#endif static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c) { return container_of(i2c, struct drm_dp_aux, ddc); @@ -957,13 +963,13 @@ static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags) { mutex_unlock(&i2c_to_aux(i2c)->hw_mutex); } - +#ifdef __linux__ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = { .lock_bus = lock_bus, .trylock_bus = trylock_bus, .unlock_bus = unlock_bus, }; - +#endif static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc) { u8 buf, count; @@ -1063,12 +1069,12 @@ void drm_dp_aux_init(struct drm_dp_aux *aux) mutex_init(&aux->hw_mutex); mutex_init(&aux->cec.lock); INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); - +#ifdef __linux__ aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; aux->ddc.retries = 3; - aux->ddc.lock_ops = &drm_dp_i2c_lock_ops; +#endif } EXPORT_SYMBOL(drm_dp_aux_init); @@ -1091,27 +1097,35 @@ EXPORT_SYMBOL(drm_dp_aux_init); int drm_dp_aux_register(struct drm_dp_aux *aux) { int ret; - +#ifdef __linux__ if (!aux->ddc.algo) +#else + if (!aux->ddc.bsddev) +#endif drm_dp_aux_init(aux); +#ifdef __linux__ aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; aux->ddc.dev.parent = aux->dev; strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), sizeof(aux->ddc.name)); - +#else + aux->ddc.bsddev = aux->dev; + strlcpy(aux->ddc.name, aux->name ? aux->name : device_get_name(aux->dev), + sizeof(aux->ddc.name)); +#endif ret = drm_dp_aux_register_devnode(aux); if (ret) return ret; - +#ifdef __linux__ ret = i2c_add_adapter(&aux->ddc); if (ret) { drm_dp_aux_unregister_devnode(aux); return ret; } - +#endif return 0; } EXPORT_SYMBOL(drm_dp_aux_register); @@ -1123,7 +1137,9 @@ EXPORT_SYMBOL(drm_dp_aux_register); void drm_dp_aux_unregister(struct drm_dp_aux *aux) { drm_dp_aux_unregister_devnode(aux); +#ifdef __linux__ i2c_del_adapter(&aux->ddc); +#endif } EXPORT_SYMBOL(drm_dp_aux_unregister); diff --git a/core/include/drm/drm_dp_helper.h b/core/include/drm/drm_dp_helper.h index 2035ac4..b937895 100644 --- a/core/include/drm/drm_dp_helper.h +++ b/core/include/drm/drm_dp_helper.h @@ -22,8 +22,10 @@ #ifndef _DRM_DP_HELPER_H_ #define _DRM_DP_HELPER_H_ - +#include +#include #include +#include #include #include diff --git a/core/include/drm/drm_print.h b/core/include/drm/drm_print.h index 2675f37..72f6c5c 100644 --- a/core/include/drm/drm_print.h +++ b/core/include/drm/drm_print.h @@ -351,14 +351,7 @@ void drm_dev_dbg(const struct device *dev, enum drm_debug_category category, * @fmt: printf() like format string. */ #define DRM_DEV_ERROR_RATELIMITED(dev, fmt, ...) \ -({ \ - static DEFINE_RATELIMIT_STATE(_rs, \ - DEFAULT_RATELIMIT_INTERVAL, \ - DEFAULT_RATELIMIT_BURST); \ - \ - if (__ratelimit(&_rs)) \ - DRM_DEV_ERROR(dev, fmt, ##__VA_ARGS__); \ -}) + DRM_DEV_ERROR(dev, fmt, ##__VA_ARGS__); #define DRM_DEV_INFO(dev, fmt, ...) \ drm_dev_printk(dev, KERN_INFO, fmt, ##__VA_ARGS__) @@ -508,13 +501,8 @@ void __drm_err(const char *format, ...); #define DRM_DEBUG_KMS_RATELIMITED(fmt, ...) \ -({ \ - static DEFINE_RATELIMIT_STATE(_rs, \ - DEFAULT_RATELIMIT_INTERVAL, \ - DEFAULT_RATELIMIT_BURST); \ - if (__ratelimit(&_rs)) \ - drm_dev_dbg(NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__); \ -}) + drm_dev_dbg(NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__) + /* * struct drm_device based WARNs diff --git a/extra_patches/files b/extra_patches/files index 0b9b022..b6549a0 100644 --- a/extra_patches/files +++ b/extra_patches/files @@ -14,6 +14,8 @@ dev/drm/core/drm_color_mgmt.c optional compat_drmcompat drm compile-with "${DR dev/drm/core/drm_connector.c optional compat_drmcompat drm compile-with "${DRM_C}" dev/drm/core/drm_crtc.c optional compat_drmcompat drm compile-with "${DRM_C}" dev/drm/core/drm_crtc_helper.c optional compat_drmcompat drm compile-with "${DRM_C}" +dev/drm/core/drm_dp_helper.c optional compat_drmcompat drm compile-with "${DRM_C}" +#dev/drm/core/drm_dp_mst_topology.c optional compat_drmcompat drm compile-with "${DRM_C}" dev/drm/core/drm_damage_helper.c optional compat_drmcompat drm compile-with "${DRM_C}" dev/drm/core/drm_drv.c optional compat_drmcompat drm compile-with "${DRM_C}" dev/drm/core/drm_dumb_buffers.c optional compat_drmcompat drm compile-with "${DRM_C}" diff --git a/extra_patches/files.bridges b/extra_patches/files.bridges index 8474123..a711ab0 100644 --- a/extra_patches/files.bridges +++ b/extra_patches/files.bridges @@ -2,8 +2,10 @@ dev/drm/bridges/dw_hdmi/aw_de2_dw_hdmi.c optional drm fdt dw_hdmi aw_de2_drm compile-with "${DRM_C}" dev/drm/bridges/dw_hdmi/rk_dw_hdmi.c optional drm fdt dw_hdmi rk_drm compile-with "${DRM_C}" dev/drm/bridges/dw_hdmi/dw_hdmi.c optional drm fdt dw_hdmi compile-with "${DRM_C}" -dev/drm/bridges/dw_hdmi/dw_hdmi_if.m optional drm fdt dw_hdmi +dev/drm/bridges/dw_hdmi/dw_hdmi_if.m optional drm fdt dw_hdmi | rk_anxdp dev/drm/bridges/dw_hdmi/dw_hdmi_phy_if.m optional drm fdt dw_hdmi # ANX6345 RGB to eDP bridge dev/drm/bridges/anx6345/anx6345.c optional fdt anx6345 compile-with "${DRM_C}" +# ANXDP for pinebookpro +dev/drm/bridges/anxdp/anx_dp.c optional fdt rk_anxdp compile-with "${DRM_C}" diff --git a/extra_patches/files.rk b/extra_patches/files.rk index 2d48550..95f67a8 100644 --- a/extra_patches/files.rk +++ b/extra_patches/files.rk @@ -4,3 +4,4 @@ dev/drm/rockchip/rk_gem.c optional drm fdt rk_drm compile-with "${DRM_C}" dev/drm/rockchip/rk_plane.c optional drm fdt rk_drm compile-with "${DRM_C}" dev/drm/rockchip/rk_vop.c optional drm fdt rk_drm compile-with "${DRM_C}" dev/drm/rockchip/rk_vop_if.m optional drm fdt rk_drm +dev/drm/rockchip/rk_anxdp.c optional drm fdt rk_anxdp compile-with "${DRM_C}" diff --git a/rockchip/rk_anxdp.c b/rockchip/rk_anxdp.c new file mode 100644 index 0000000..4c49d95 --- /dev/null +++ b/rockchip/rk_anxdp.c @@ -0,0 +1,245 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Jesper Schmitz Mouridsen + * Copyright (c) 2019 Jonathan A. Kollasch + + * 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. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +struct rk_anxdp_softc { + struct anxdp_softc sc_base; + device_t dev; + struct syscon *grf; + struct drm_encoder sc_encoder; + clk_t pclk; + clk_t dpclk; + clk_t grfclk; +}; + +DECLARE_CLASS(rk_anxdp_driver); + +#include "syscon_if.h" +#include "dw_hdmi_if.h" +#define RK3399_GRF_SOC_CON20 0x6250 +#define EDP_LCDC_SEL BIT(5) + + +static struct ofw_compat_data rkedp_compat_data[] = { + {"rockchip,rk3399-edp", 1}, + {NULL, 0} + +}; + +static struct resource_spec rk_anxdp_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1,0} +}; +static int rk_anxdp_probe(device_t dev); +static int rk_anxdp_attach(device_t dev); + +static int rk_anxdp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm); + +#define to_rk_anxdp_softc(x) container_of(x, struct rk_anxdp_softc, sc_base) +#define to_anxdp_encoder(x) container_of(x, struct anxdp_softc, sc_encoder) + + +static bool +rk_anxdp_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void +rk_anxdp_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, struct drm_display_mode *adjusted) +{ +} + +static void +rk_anxdp_encoder_enable(struct drm_encoder *encoder) +{ +} + +static void +rk_anxdp_encoder_disable(struct drm_encoder *encoder) +{ +} + +static void +rk_anxdp_encoder_prepare(struct drm_encoder *encoder,struct drm_atomic_state * state) +{ + struct rk_anxdp_softc * const sc =container_of(encoder,struct rk_anxdp_softc,sc_encoder); + + SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON20, (EDP_LCDC_SEL | (EDP_LCDC_SEL) <<16)); + + +} + + + +static const struct drm_encoder_funcs rk_anxdp_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static const struct drm_encoder_helper_funcs rk_anxdp_encoder_helper_funcs = { + .atomic_enable = rk_anxdp_encoder_prepare, + .mode_fixup = rk_anxdp_encoder_mode_fixup, + .mode_set = rk_anxdp_encoder_mode_set, + .enable = rk_anxdp_encoder_enable, + .disable = rk_anxdp_encoder_disable, +}; + +#define to_rk_anxdp_softc(x) container_of(x, struct rk_anxdp_softc, sc_base) +#define to_rk_anxdp_encoder(x) container_of(x, struct rk_anxdp_softc, sc_encoder) + +static device_method_t +rk_anxdp_methods[] = { + DEVMETHOD(device_probe, rk_anxdp_probe), + DEVMETHOD(device_attach, rk_anxdp_attach), + DEVMETHOD(dw_hdmi_add_encoder, rk_anxdp_add_encoder), + DEVMETHOD_END +}; + +static int +rk_anxdp_probe(device_t dev){ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, rkedp_compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "RockChip Analogix DP"); + return (BUS_PROBE_DEFAULT); +} +static int +rk_anxdp_attach(device_t dev) +{ + struct rk_anxdp_softc *sc; + phandle_t node; + int error; + + + sc = device_get_softc(dev); + + node = ofw_bus_get_node(dev); + if (bus_alloc_resources(dev,rk_anxdp_spec, sc->sc_base.res)!=0) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->sc_base.sc_flags |= ANXDP_FLAG_ROCKCHIP; + + + error = clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk); + if (error!=0) { + device_printf(dev,"could not get pclk error:%d\n",error); + return -1; + } + error = clk_enable(sc->pclk); + if (error!=0) { + device_printf(dev,"could not enable pclk error:%d\n",error); + return -1; + } + error = clk_get_by_ofw_name(dev, 0, "dp", &sc->dpclk); + if (error!=0) { + device_printf(dev,"could not get dp clock error:%d\n",error); + return -1; + } + error = clk_enable(sc->dpclk); + if (error!=0) { + device_printf(dev,"could not enable dp error:%d\n",error); + return -1; + } + error = clk_get_by_ofw_name(dev, 0, "grf", &sc->grfclk); + if (error!=0) { + device_printf(dev,"could not get grf clock error:%d\n",error); + return -1; + } + error = clk_enable(sc->grfclk); + if (error!=0) { + device_printf(dev,"could not enable grp clok error:%d\n",error); + return -1; + } + error = syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf); + if (error != 0) { + device_printf(dev,"cannot get grf syscon: %d\n", error); + return (ENXIO); + } + + sc->dev=dev; + OF_device_register_xref(OF_xref_from_node(node),sc->dev); + sc->sc_base.sc_dev=dev; + anxdp_attach(&sc->sc_base); + return (0); + +} + +static int +rk_anxdp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm) +{ + struct rk_anxdp_softc *sc; + int error = 0; + sc = device_get_softc(dev); + + drm_encoder_helper_add(&sc->sc_encoder,&rk_anxdp_encoder_helper_funcs); + sc->sc_encoder.possible_crtcs = drm_crtc_mask(crtc); + + drm_encoder_init(drm, &sc->sc_encoder, &rk_anxdp_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP; + + error = anxdp_bind(&sc->sc_base, &sc->sc_encoder); + + return error; +} + + +DEFINE_CLASS_1(rk_anxdp, rk_anxdp_driver, rk_anxdp_methods, + sizeof(struct rk_anxdp_softc), anxdp_driver); + +EARLY_DRIVER_MODULE(rk_anxdp, simplebus, rk_anxdp_driver, +0,0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_EARLY); +MODULE_VERSION(rk_anxdp, 1); diff --git a/rockchip/rk_vop.c b/rockchip/rk_vop.c index d1c5137..048c4c2 100644 --- a/rockchip/rk_vop.c +++ b/rockchip/rk_vop.c @@ -102,15 +102,21 @@ static struct resource_spec rk_vop_spec[] = { }; static void -rk_vop_set_polarity(struct rk_vop_softc *sc, uint32_t pin_polarity) +rk_vop_set_polarity(struct rk_vop_softc *sc, uint32_t pin_polarity,int connector_type) { uint32_t reg; - - /* HDMI */ reg = VOP_READ(sc, RK3399_DSP_CTRL1); - reg &= ~DSP_CTRL1_HDMI_POL_M; - reg |= pin_polarity << DSP_CTRL1_HDMI_POL_S; + if(connector_type == DRM_MODE_CONNECTOR_eDP) { + reg &= ~RK3399_VOP_EDP_POL; + reg |= pin_polarity << RK3399_DSP_BG; + } + else { + /* HDMI */ + reg &= ~DSP_CTRL1_HDMI_POL_M; + reg |= pin_polarity << DSP_CTRL1_HDMI_POL_S; + } VOP_WRITE(sc, RK3399_DSP_CTRL1, reg); + } static int @@ -461,17 +467,26 @@ rk_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) pol |= (1 << HSYNC_POSITIVE); if (adj->flags & DRM_MODE_FLAG_PVSYNC) pol |= (1 << VSYNC_POSITIVE); - rk_vop_set_polarity(sc, pol); + + rk_vop_set_polarity(sc, pol,sc->connector_type); + + /* Remove standby bit */ reg = VOP_READ(sc, RK3399_SYS_CTRL); reg &= ~SYS_CTRL_STANDBY_EN; VOP_WRITE(sc, RK3399_SYS_CTRL, reg); - /* Enable HDMI output only. */ reg = VOP_READ(sc, RK3399_SYS_CTRL); reg &= ~SYS_CTRL_ALL_OUT_EN; - reg |= SYS_CTRL_HDMI_OUT_EN; + switch(sc->connector_type) { + case DRM_MODE_CONNECTOR_eDP: { + reg |= SYS_CTRL_EDP_OUT_EN; + break; + } + default: + reg |= SYS_CTRL_HDMI_OUT_EN; + } VOP_WRITE(sc, RK3399_SYS_CTRL, reg); dprintf("SYS_CTRL %x\n", VOP_READ(sc, RK3399_SYS_CTRL)); @@ -565,23 +580,24 @@ rk_vop_add_encoder(struct rk_vop_softc *sc, struct drm_device *drm) { phandle_t node; device_t dev; - int ret; + int ret,i; node = ofw_bus_get_node(sc->dev); if (node == 0) return (ENOENT); + for(i=1;i<=2;i++) { + dev = ofw_graph_get_device_by_port_ep(ofw_bus_get_node(sc->dev),0, i); + if (dev != NULL) { - dev = ofw_graph_get_device_by_port_ep(ofw_bus_get_node(sc->dev), - 0, 2 /* HDMI */); - if (dev == NULL) - return (ENOENT); - - ret = DW_HDMI_ADD_ENCODER(dev, &sc->crtc, drm); - if (ret == 0) - return (ENODEV); - - sc->outport = dev; + sc->connector_type = strncmp(device_get_name(dev),"rk_anxdp",8)==0 ? DRM_MODE_CONNECTOR_eDP : DRM_MODE_CONNECTOR_HDMIA; + ret = DW_HDMI_ADD_ENCODER(dev, &sc->crtc, drm); + if (ret == 0) + return (ENODEV); + sc->outport = dev; + break; + } + } return (0); } @@ -630,7 +646,6 @@ static driver_t rk_vop_driver = { sizeof(struct rk_vop_softc) }; -static devclass_t rk_vop_devclass; EARLY_DRIVER_MODULE(rk_vop, simplebus, rk_vop_driver, - rk_vop_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); MODULE_VERSION(rk_vop, 1); diff --git a/rockchip/rk_vop.h b/rockchip/rk_vop.h index b6478bc..c459f9e 100644 --- a/rockchip/rk_vop.h +++ b/rockchip/rk_vop.h @@ -54,6 +54,7 @@ #define DSP_CTRL1_MIPI_POL_M (0xf << DSP_CTRL1_MIPI_POL_S) #define DSP_CTRL1_HDMI_POL_S 20 #define DSP_CTRL1_HDMI_POL_M (0xf << DSP_CTRL1_HDMI_POL_S) +#define RK3399_VOP_EDP_POL ((~((~0)<<((27)+1)))&((~0)<<(24))) #define RK3399_DSP_BG 0x0018 #define RK3399_MCU_CTRL 0x001c #define RK3399_WB_CTRL0 0x0020 @@ -314,6 +315,7 @@ struct rk_vop_softc { hwreset_t hwreset_dclk; struct rk_vop_plane planes[2]; + int connector_type; struct drm_pending_vblank_event *event; struct drm_device *drm; struct drm_crtc crtc;