diff --git a/bridges/anxdp/anx_dp.c b/bridges/anxdp/anx_dp.c new file mode 100644 index 0000000..bd434da --- /dev/null +++ b/bridges/anxdp/anx_dp.c @@ -0,0 +1,765 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 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 "syscon_if.h" +#include "iicbus_if.h" +#include "anx_dp.h" + +#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)) + + +static void anxdp_init_aux(struct anxdp_softc * sc); + + +static void edp_connector_destroy(struct drm_connector *connector); +static void anxdp_bringup(struct anxdp_softc * const sc); + + +static void anxdp_init_hpd(struct anxdp_softc * const sc); +static inline const bool isrockchip(struct anxdp_softc * const sc); +static ssize_t anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg); +static void anxdp_analog_power_up_all(struct anxdp_softc * const sc); +static void anxdp_init_hpd(struct anxdp_softc * const sc); +static int anxdp_await_pll_lock(struct anxdp_softc * const sc); +static void edp_bridge_enable(struct drm_bridge *bridge); +static void edp_macro_reset(struct anxdp_softc * const sc); +static void edp_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode); +static bool edp_bridge_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +static void edp_bridge_pre_enable(struct drm_bridge *bridge); +static void edp_bridge_post_disable(struct drm_bridge *bridge); +static int edp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags); +static void edp_bridge_disable(struct drm_bridge *bridge); +static void edp_train_link(struct anxdp_softc * const sc); + +static const struct drm_bridge_funcs edp_bridge_funcs = { + .attach = edp_bridge_attach, + .enable = edp_bridge_enable, + .pre_enable = edp_bridge_pre_enable, + .disable = edp_bridge_disable, + .post_disable = edp_bridge_post_disable, + .mode_set = edp_bridge_mode_set, + .mode_fixup = edp_bridge_mode_fixup, + +}; + +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; + +} + + +#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) +struct drm_dp_link { + unsigned char revision; + unsigned int rate; + unsigned int num_lanes; + unsigned long capabilities; +}; + + +static enum drm_connector_status +edp_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 int edp_connector_get_modes(struct drm_connector *connector) +{ + int error; + struct edid *pedid = NULL; + + pedid = drm_get_edid(connector, connector->ddc); + + if (pedid == NULL) + return 0; + + error = drm_add_edid_modes(connector, pedid); + if (pedid != NULL) + kfree(pedid); + + return error; +} + + +static const struct drm_connector_funcs edp_connector_funcs = { + .detect = edp_connector_detect, + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = edp_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, + // .late_register = edp_connector_late_register, +}; +static const struct drm_connector_helper_funcs edp_connector_helper_funcs = { + .get_modes = edp_connector_get_modes, +}; +void +anxdp_add_bridge(struct anxdp_softc *sc,struct drm_encoder *encoder) +{ + sc->sc_bridge.funcs = &edp_bridge_funcs; + drm_bridge_attach(encoder, &sc->sc_bridge, NULL,0); +} + + +static int +edp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) +{ + struct anxdp_softc *sc; + sc = container_of(bridge, struct anxdp_softc, sc_bridge); + + sc->sc_connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + sc->sc_connector.interlace_allowed = 0; + sc->sc_connector.doublescan_allowed = 0; + + drm_connector_helper_add(&sc->sc_connector, + &edp_connector_helper_funcs); + + drm_connector_init_with_ddc(bridge->dev,&sc->sc_connector, + &edp_connector_funcs, DRM_MODE_CONNECTOR_eDP,&sc->sc_dpaux.ddc); + + drm_connector_attach_encoder(&sc->sc_connector, &sc->sc_encoder); + + return (0); +} + + + + + +static void +edp_bridge_enable(struct drm_bridge *bridge) +{ + + struct anxdp_softc * sc = container_of(bridge,struct anxdp_softc,sc_bridge); + + 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); + edp_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 // Audio is not tested on FreeBSD. + ANXDP_READ(sc, + if (sc->sc_connector.monitor_audio) + anxdp_audio_init(sc); +#endif + } + + +static void +edp_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static void edp_bridge_pre_enable(struct drm_bridge *bridge) +{ +} + +static void +edp_bridge_disable(struct drm_bridge *bridge) +{ +} + + + +static void edp_bridge_post_disable(struct drm_bridge *bridge) +{ +} + + +static void +edp_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) +{ + struct anxdp_softc * sc = container_of(bridge,struct anxdp_softc,sc_bridge); + + memcpy(&sc->sc_curmode, mode, sizeof(struct drm_display_mode)); + +} + +static bool +edp_bridge_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void edp_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 +edp_link_start(struct anxdp_softc * const sc, struct drm_dp_link * const link) +{ + uint8_t training[4]; + uint32_t val; + u8 values[2]; + int err; + 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); + values[0] = drm_dp_link_rate_to_bw_code(link->rate); + values[1] = link->num_lanes; + + if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + + + + err = drm_dp_dpcd_write(&sc->sc_dpaux, DP_LINK_BW_SET, values, sizeof(values)); + if (err < 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) { + printf("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 +edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_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; + } + + 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 +edp_process_eq(struct anxdp_softc * const sc, struct drm_dp_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 +edp_train_link(struct anxdp_softc * const sc) +{ + int err; + u8 values[3]; + u8 value; + struct drm_dp_link link; + + edp_macro_reset(sc); + drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, values, sizeof(values)); + + 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.capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; + + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (link.revision < 0x11) + return; + + err = drm_dp_dpcd_readb(&sc->sc_dpaux, DP_SET_POWER, &value); + if (err <0) + return; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_SET_POWER, value); + + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + usleep_range(10040, 2000); + + if (DP_RECEIVER_CAP_SIZE != drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, + sc->sc_dpcd, DP_RECEIVER_CAP_SIZE)) + return; + + edp_link_start(sc, &link); + edp_process_clock_recovery(sc, &link); + edp_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_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_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 inline const bool +isrockchip(struct anxdp_softc * const sc) +{ + return (sc->sc_flags & ANXDP_FLAG_ROCKCHIP) != 0; +} + +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) +{ + phandle_t node; + node = ofw_bus_get_node(sc->sc_dev); + + sc->sc_dpaux.name = "DP Aux"; + sc->sc_dpaux.dev = sc->sc_dev; + sc->sc_dpaux.transfer=anxdp_dp_aux_transfer; + + if (drm_dp_aux_register(&sc->sc_dpaux) != 0) { + device_printf(sc->sc_dev, "registering DP Aux failed\n"); + } + OF_device_register_xref(OF_xref_from_node(node),sc->sc_dev); + anxdp_bringup(sc); + return 0; +} +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); +} + + + +void +anxdp_init_aux(struct anxdp_softc *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 device_method_t +anxdp_methods[] = { +}; + + +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..3c90bff --- /dev/null +++ b/bridges/anxdp/anx_dp.h @@ -0,0 +1,237 @@ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 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$ + */ + + +#ifndef __ANXEDP_H__ +#define __ANXEDP_H__ +#include +#include +#undef CONFIG_DRM_DP_CEC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __BITS(hi,lo) ((~((~0)<<((hi)+1)))&((~0)<<(lo))) +#define __BIT BIT +#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 ANXDP_AUDIO 0 +#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_I2C_TRANSACTION (0x0 << 3) + +#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 + + + + +struct anxdp_softc { + struct mtx mtx; + device_t iicbus; + struct device* sc_dev; + + struct resource *res[2]; + u_int sc_flags; +#define ANXDP_FLAG_ROCKCHIP __BIT(0) + struct drm_connector sc_connector; + struct drm_encoder sc_encoder; + struct drm_dp_aux sc_dpaux; + struct drm_panel * sc_panel; + uint8_t sc_dpcd[DP_RECEIVER_CAP_SIZE]; + struct drm_bridge sc_bridge; + struct drm_display_mode sc_curmode; +}; + +#define ANXDP_LOCK(sc) mtx_lock(&(sc)->mtx) +#define ANXDP_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#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_edp_connector(x) container_of(x, struct anxdp_connector, base) +int anxdp_attach(struct anxdp_softc *sc); +void anxdp_add_bridge(struct anxdp_softc *sc,struct drm_encoder *encoder); + +DECLARE_CLASS(anxdp_driver); +#endif /* ANXEDP_H */ + diff --git a/core/drm_dp_helper.c b/core/drm_dp_helper.c index 19c99dd..f32acba 100644 --- a/core/drm_dp_helper.c +++ b/core/drm_dp_helper.c @@ -957,13 +957,13 @@ static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags) { mutex_unlock(&i2c_to_aux(i2c)->hw_mutex); } - +#ifndef __FreeBSD__ 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; @@ -1067,8 +1067,9 @@ void drm_dp_aux_init(struct drm_dp_aux *aux) aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; aux->ddc.retries = 3; - +#ifndef __FreeBSD__ aux->ddc.lock_ops = &drm_dp_i2c_lock_ops; +#endif } EXPORT_SYMBOL(drm_dp_aux_init); diff --git a/core/drm_dp_mst_topology.c b/core/drm_dp_mst_topology.c index 1e26b89..d7916dc 100644 --- a/core/drm_dp_mst_topology.c +++ b/core/drm_dp_mst_topology.c @@ -27,7 +27,9 @@ #include #include #include +#ifdef __linux__ #include +#endif #if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) #include @@ -2116,9 +2118,10 @@ static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb, int drm_dp_mst_connector_late_register(struct drm_connector *connector, struct drm_dp_mst_port *port) { +#ifndef __FreeBSD__ DRM_DEBUG_KMS("registering %s remote bus for %s\n", port->aux.name, connector->kdev->kobj.name); - +#endif port->aux.dev = connector->kdev; return drm_dp_aux_register_devnode(&port->aux); } @@ -2136,8 +2139,10 @@ EXPORT_SYMBOL(drm_dp_mst_connector_late_register); void drm_dp_mst_connector_early_unregister(struct drm_connector *connector, struct drm_dp_mst_port *port) { +#ifndef __FreeBSD__ DRM_DEBUG_KMS("unregistering %s remote bus for %s\n", port->aux.name, connector->kdev->kobj.name); +#endif drm_dp_aux_unregister_devnode(&port->aux); } EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister); @@ -4391,6 +4396,7 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, return ret; } +#ifndef __FreeBSD__ static int do_get_act_status(struct drm_dp_aux *aux) { int ret; @@ -4402,7 +4408,6 @@ static int do_get_act_status(struct drm_dp_aux *aux) return status; } - /** * drm_dp_check_act_status() - Polls for ACT handled status. * @mgr: manager to use @@ -4445,7 +4450,7 @@ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) return 0; } EXPORT_SYMBOL(drm_dp_check_act_status); - +#endif /** * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. * @clock: dot clock for the mode @@ -5359,8 +5364,9 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; aux->ddc.dev.parent = aux->dev; +#ifndef __FreeBSD__ aux->ddc.dev.of_node = aux->dev->of_node; - +#endif strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), sizeof(aux->ddc.name)); diff --git a/core/include/drm/drm_dp_helper.h b/core/include/drm/drm_dp_helper.h index 2035ac4..ea9b8a7 100644 --- a/core/include/drm/drm_dp_helper.h +++ b/core/include/drm/drm_dp_helper.h @@ -22,7 +22,8 @@ #ifndef _DRM_DP_HELPER_H_ #define _DRM_DP_HELPER_H_ - +#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/drmkpi/include/linux/i2c.h b/drmkpi/include/linux/i2c.h index 432981d..dfdb60f 100644 --- a/drmkpi/include/linux/i2c.h +++ b/drmkpi/include/linux/i2c.h @@ -29,49 +29,93 @@ * */ + #ifndef __DRMCOMPAT_LINUX_I2C_H__ #define __DRMCOMPAT_LINUX_I2C_H__ +#include + #include #include #include +#define I2C_NAME_SIZE 20 /* I2C compatibility. */ #define I2C_M_RD IIC_M_RD #define I2C_M_WR IIC_M_WR #define I2C_M_NOSTART IIC_M_NOSTART - +#define I2C_M_STOP 0x8000 struct i2c_msg { uint16_t addr; uint16_t flags; uint16_t len; uint8_t *buf; -}; +}; +#define I2C_CLASS_DDC 0x01 -struct i2c_adapter -{ - device_t bsddev; - char name[20]; - +/* + * I2C_FUNC_*: i2c_adapter functionality bits + */ +#define I2C_FUNC_I2C 0x01 +#define I2C_FUNC_NOSTART 0x02 +#define I2C_FUNC_SMBUS_EMUL 0x04 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x08 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x10 +#define I2C_FUNC_10BIT_ADDR 0x20 + + +/* + * struct i2c_msg: A single i2c message request on a particular + * address. Read if I2C_M_RD is set, write otherwise. + */ +struct i2c_adapter; + /* struct i2c_algorithm: A procedure for transferring an i2c message on + * an i2c bus, along with a set of flags describing its functionality. + */ +struct i2c_algorithm { + int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *,int); + uint32_t (*functionality)(struct i2c_adapter *); }; + +struct i2c_adapter { + char name[I2C_NAME_SIZE]; + const struct i2c_algorithm *algo; + void *algo_data; + int retries; + struct module *owner; + unsigned int class; /* I2C_CLASS_* */ + device_t bsddev; + struct { + device_t parent; + } dev; + void *i2ca_adapdata; + +}; + + + static inline struct i2c_adapter * i2c_bsd_adapter(device_t dev) { struct i2c_adapter *adap; - + adap = malloc(sizeof(struct i2c_adapter), M_TEMP, M_WAITOK); adap->bsddev = dev; strcpy(adap->name, "emulated-i2c"); return (adap); } -static inline int +static inline int i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { + + if(adap && adap->algo + && !adap->bsddev) + return (*adap->algo->master_xfer)(adap, msgs, num); + struct iic_msg *bsd_msgs; int i, ret; - bsd_msgs = malloc(sizeof(struct iic_msg) * num, M_TEMP, M_WAITOK); memcpy(bsd_msgs, msgs, sizeof(struct iic_msg) * num); /* Linux uses 7-bit addresses but FreeBSD 8-bit */ @@ -81,12 +125,43 @@ i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int num) bsd_msgs[i].len = msgs[i].len; bsd_msgs[i].buf = msgs[i].buf; } - + ret = iicbus_transfer(adap->bsddev, bsd_msgs, num); free(bsd_msgs, M_TEMP); if (ret != 0) return (-ret); + return (num); + +} +/* + * Adapter management. We don't register these in a global database + * like Linux, so these are just stubs. + */ +static inline int +i2c_add_adapter(struct i2c_adapter *adapter __unused) +{ + + return 0; +} + +static inline void +i2c_del_adapter(struct i2c_adapter *adapter __unused) +{ } - + +static inline void * +i2c_get_adapdata(const struct i2c_adapter *adapter) +{ + + return adapter->i2ca_adapdata; +} + +static inline void +i2c_set_adapdata(struct i2c_adapter *adapter, void *data) +{ + + adapter->i2ca_adapdata = data; +} + #endif /* __DRMCOMPAT_LINUX_I2C_H__ */ diff --git a/extra_patches/files b/extra_patches/files index f169e85..2e91c8b 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..7b4eac9 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_phy_if.m optional drm fdt dw_hdmi +dev/drm/bridges/dw_hdmi/dw_hdmi_if.m optional drm fdt dw_hdmi | rk_edp +dev/drm/bridges/dw_hdmi/dw_hdmi_phy_if.m optional drm fdt dw_hdmi | rk_edp # 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_edp compile-with "${DRM_C}" diff --git a/extra_patches/files.rk b/extra_patches/files.rk index 2d48550..31c915a 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_edp.c optional drm fdt rk_edp compile-with "${DRM_C}" diff --git a/rockchip/rk_edp.c b/rockchip/rk_edp.c new file mode 100644 index 0000000..f6c0787 --- /dev/null +++ b/rockchip/rk_edp.c @@ -0,0 +1,200 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 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 +__FBSDID("$FreeBSD$"); +#include "rk_edp.h" +#include "syscon_if.h" +#include "dev/drm/bridges/anxdp/anx_dp.h" +#include "dw_hdmi_if.h" +//#include "iicbus_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_edp_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1,0} +}; +static int rk_edp_probe(device_t dev); +static int rk_edp_attach(device_t dev); + +static int rk_edp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm); + +#define to_rk_anxdp_softc(x) container_of(x, struct rk_edp_softc, sc_base) +#define to_anxdp_encoder(x) container_of(x, struct anxdp_softc, sc_encoder) + + +static void rk_anxdp_select_input(struct rk_edp_softc *sc, u_int crtc_index) +{ + const uint32_t write_mask = EDP_LCDC_SEL << 16; + const uint32_t write_val = crtc_index == 0 ? EDP_LCDC_SEL : 0; + SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON20, write_mask | write_val); + +} + + +static void +rk_anxdp_encoder_prepare(struct drm_encoder *encoder) +{ + printf("%s:%d %s\n",__FILE__,__LINE__,__FUNCTION__); + struct anxdp_softc *sc_base; + struct rk_edp_softc *sc; + sc_base = container_of(encoder, struct anxdp_softc, sc_encoder); + sc = container_of(sc_base, struct rk_edp_softc, sc_base); + + const u_int crtc_index = drm_crtc_index(encoder->crtc); + + rk_anxdp_select_input(sc, crtc_index); +} + +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 = { + .prepare = rk_anxdp_encoder_prepare, +}; + +#define to_rk_edp_softc(x) container_of(x, struct rk_edp_softc, sc_base) +#define to_rk_edp_encoder(x) container_of(x, struct rk_edp_softc, sc_encoder) + +static device_method_t +rk_edp_methods[] = { + DEVMETHOD(device_probe, rk_edp_probe), + DEVMETHOD(device_attach, rk_edp_attach), + DEVMETHOD(dw_hdmi_add_encoder, rk_edp_add_encoder), + DEVMETHOD_END +}; + +static int +rk_edp_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 edp"); + return (BUS_PROBE_DEFAULT); +} +static int +rk_edp_attach(device_t dev) +{ + struct rk_edp_softc *sc; + phandle_t node; + int error; + + + sc = device_get_softc(dev); + mtx_init(&sc->sc_base.mtx, device_get_nameunit(dev), "rk_edp", MTX_DEF); + node = ofw_bus_get_node(dev); + if (bus_alloc_resources(dev,rk_edp_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; + + sc->sc_base.sc_dev=dev; + anxdp_attach(&sc->sc_base); + return (0); + +} + +static int +rk_edp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm) +{ + struct rk_edp_softc *sc; + sc = device_get_softc(dev); + drm_encoder_helper_add(&sc->sc_base.sc_encoder,&rk_anxdp_encoder_helper_funcs); + sc->sc_base.sc_encoder.possible_crtcs = drm_crtc_mask(crtc); + drm_encoder_init(drm, &sc->sc_base.sc_encoder, &rk_anxdp_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + rk_anxdp_select_input(sc,crtc->index); + anxdp_add_bridge(&sc->sc_base,&sc->sc_base.sc_encoder); + return (0); +} + + +static devclass_t rk_edp_devclass; +DEFINE_CLASS_1(rk_edp, rk_edp_driver, rk_edp_methods, + sizeof(struct rk_edp_softc), anxdp_driver); + +EARLY_DRIVER_MODULE(rk_edp, simplebus, rk_edp_driver,rk_edp_devclass, + 0,0,BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_EARLY); +MODULE_VERSION(rk_edp, 1); diff --git a/rockchip/rk_edp.h b/rockchip/rk_edp.h new file mode 100644 index 0000000..30139ae --- /dev/null +++ b/rockchip/rk_edp.h @@ -0,0 +1,63 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 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$ + */ + +#ifndef __RK_EDP_H__ +#define __RK_EDP_H__ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct rk_edp_softc { + struct anxdp_softc sc_base; + device_t dev; + struct syscon *grf; + clk_t pclk; + clk_t dpclk; + clk_t grfclk; +}; + +DECLARE_CLASS(rk_edp_driver); +#endif /* __RK_EDP_H__ */ diff --git a/rockchip/rk_vop.c b/rockchip/rk_vop.c index d1c5137..0b7935e 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 @@ -284,8 +290,8 @@ rk_vop_attach(device_t dev) } if (bus_setup_intr(dev, sc->res[1], - INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_vop_intr, sc, - &sc->intrhand)) { + INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_vop_intr, sc, + &sc->intrhand)) { bus_release_resources(dev, rk_vop_spec, sc->res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); @@ -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_edp",6)==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); } diff --git a/rockchip/rk_vop.h b/rockchip/rk_vop.h index b6478bc..4ceb753 100644 --- a/rockchip/rk_vop.h +++ b/rockchip/rk_vop.h @@ -134,6 +134,7 @@ #define RK3399_WIN2_DSP_ST3 0x00f8 #define RK3399_WIN2_FADING_CTRL 0x00fc +#define RK3399_VOP_EDP_POL ((~((~0)<<((27)+1)))&((~0)<<(24))) #define RK3399_WIN3_CTRL0 0x0100 #define RK3399_WIN3_CTRL1 0x0104 #define RK3399_WIN3_VIR0_1 0x0108 @@ -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;