From 08925508c84b8a73b82628ea8fc7a23701e8e6ad Mon Sep 17 00:00:00 2001 From: Jesper Schmitz Mouridsen Date: Mon, 9 May 2022 08:46:12 +0200 Subject: [PATCH 1/2] Anxdp support reworked panfrost works compiled as device on pbp now. --- bridges/anxdp/anx_dp.c | 765 +++++++++++++++++++++++++++++++ bridges/anxdp/anx_dp.h | 237 ++++++++++ core/drm_dp_mst_topology.c | 22 + core/include/drm/drm_dp_helper.h | 3 +- core/include/drm/drm_print.h | 18 +- drmkpi/include/linux/i2c.h | 100 +++- extra_patches/files | 2 + extra_patches/files.bridges | 4 +- extra_patches/files.rk | 1 + rockchip/rk_edp.c | 200 ++++++++ rockchip/rk_edp.h | 63 +++ rockchip/rk_vop.c | 54 ++- rockchip/rk_vop.h | 2 + 13 files changed, 1416 insertions(+), 55 deletions(-) create mode 100644 bridges/anxdp/anx_dp.c create mode 100644 bridges/anxdp/anx_dp.h create mode 100644 rockchip/rk_edp.c create mode 100644 rockchip/rk_edp.h 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_mst_topology.c b/core/drm_dp_mst_topology.c index 1e26b89..6c88bd3 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,11 @@ 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 +2140,11 @@ 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); @@ -4424,10 +4431,23 @@ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) */ const int timeout_ms = 3000; int ret, status; +#ifdef __FreeBSD__ + int tries=0; + status = do_get_act_status(mgr->aux); + while(!(status & DP_PAYLOAD_ACT_HANDLED || status < 0)) { + DELAY(200); + status = do_get_act_status(mgr->aux); + tries++; + if(tries >=15000) + ret = -ETIMEDOUT; + + } +#elif defined( __linux__) ret = readx_poll_timeout(do_get_act_status, mgr->aux, status, status & DP_PAYLOAD_ACT_HANDLED || status < 0, 200, timeout_ms * USEC_PER_MSEC); +#endif if (ret < 0 && status >= 0) { DRM_ERROR("Failed to get ACT after %dms, last status: %02x\n", timeout_ms, status); @@ -5359,7 +5379,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..67103a7 100644 --- a/drmkpi/include/linux/i2c.h +++ b/drmkpi/include/linux/i2c.h @@ -35,11 +35,20 @@ #include #include #include - +#include /* 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 0x0004 +/* No need for us */ +#define I2C_FUNC_I2C 0 +#define I2C_FUNC_SMBUS_EMUL 0 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0 +#define I2C_FUNC_10BIT_ADDR 0 + +#define I2C_CLASS_DDC 0x8 struct i2c_msg { uint16_t addr; @@ -52,15 +61,35 @@ struct i2c_adapter { device_t bsddev; char name[20]; + const struct i2c_lock_operations *lock_ops; + const struct i2c_algorithm *algo; + void *algo_data; + int retries; + struct module *owner; + unsigned int class; /* I2C_CLASS_* */ + struct { + device_t parent; + } dev; }; +struct i2c_algorithm { + int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int); + uint32_t (*functionality)(struct i2c_adapter *); +}; + +struct i2c_lock_operations { + void (*lock_bus)(struct i2c_adapter *, unsigned int); + int (*trylock_bus)(struct i2c_adapter *, unsigned int); + void (*unlock_bus)(struct i2c_adapter *, unsigned int); +}; + 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 = malloc(sizeof(struct i2c_adapter), M_TEMP, M_WAITOK | M_ZERO); adap->bsddev = dev; strcpy(adap->name, "emulated-i2c"); return (adap); @@ -69,24 +98,57 @@ i2c_bsd_adapter(device_t dev) static inline int i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int 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 */ - for (i = 0; i < num; i++) { - bsd_msgs[i].slave = msgs[i].addr << 1; - bsd_msgs[i].flags = msgs[i].flags; - bsd_msgs[i].len = msgs[i].len; - bsd_msgs[i].buf = msgs[i].buf; + + int ret,retries = 0; + retries = adap->retries == 0 ? 1 : adap->retries; + + if (adap->lock_ops) + adap->lock_ops->lock_bus(adap, 0); + if (adap->algo != NULL && adap->algo->master_xfer != NULL) { + for (; retries != 0; retries--) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + } + + } else { + struct iic_msg *bsd_msgs; + int i; + + bsd_msgs = malloc(sizeof(struct iic_msg) * num, M_TEMP, M_WAITOK); + memcpy(bsd_msgs, msgs, sizeof(struct iic_msg) * num); + /*inux uses 7-bit addresses but FreeBSD 8-bit */ + for (i = 0; i < num; i++) { + bsd_msgs[i].slave = msgs[i].addr << 1; + bsd_msgs[i].flags = msgs[i].flags; + 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) + ret = (-ret); } - - ret = iicbus_transfer(adap->bsddev, bsd_msgs, num); - free(bsd_msgs, M_TEMP); - if (ret != 0) - return (-ret); - return (num); + if (adap->lock_ops) + adap->lock_ops->unlock_bus(adap, 0); + if (ret < 0) + return (ret); + + return (num); + + +} +static inline int +i2c_add_adapter(struct i2c_adapter *adapter __unused) +{ + + return 0; +} + +static inline void +i2c_del_adapter(struct i2c_adapter *adapter __unused) +{ } #endif /* __DRMCOMPAT_LINUX_I2C_H__ */ diff --git a/extra_patches/files b/extra_patches/files index 0b9b022..8ba547a 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..ad70a92 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_edp 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_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..6fee9b2 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_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..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; From 6ccdf47dd5398ec6518e0ca655989ea83010e556 Mon Sep 17 00:00:00 2001 From: Jesper Schmitz Mouridsen Date: Tue, 4 Oct 2022 19:42:59 +0200 Subject: [PATCH 2/2] Minimize modifications to drm and i2c Do not modify i2c.h Do not include drm_dp_mst_topolygy.c, modify drm_dp_helper to not currently support aux->remote true instead. Reimplement the aux_transfer as a iic_transfer, note that it has a read buffer of max 16 bytes. Rename to rk_anxdp.c to follow netbsd. Also the anx_dp.c should be more diffable to the NetBSD version. --- bridges/anxdp/anx_dp.c | 1189 ++++++++++++++++++++--------- bridges/anxdp/anx_dp.h | 261 ++----- core/drm_dp_helper.c | 48 +- core/drm_dp_mst_topology.c | 22 - core/include/drm/drm_dp_helper.h | 1 + drmkpi/include/linux/i2c.h | 100 +-- extra_patches/files | 2 +- extra_patches/files.bridges | 4 +- extra_patches/files.rk | 2 +- rockchip/{rk_edp.c => rk_anxdp.c} | 147 ++-- rockchip/rk_edp.h | 63 -- rockchip/rk_vop.c | 9 +- 12 files changed, 1037 insertions(+), 811 deletions(-) rename rockchip/{rk_edp.c => rk_anxdp.c} (52%) delete mode 100644 rockchip/rk_edp.h diff --git a/bridges/anxdp/anx_dp.c b/bridges/anxdp/anx_dp.c index bd434da..bdc689f 100644 --- a/bridges/anxdp/anx_dp.c +++ b/bridges/anxdp/anx_dp.c @@ -1,9 +1,10 @@ +/* $NetBSD: anx_dp.c,v 1.5 2021/12/19 12:43:37 riastradh Exp $ */ + /*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021 Jesper Schmitz Mouridsen + * 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: @@ -13,102 +14,253 @@ * 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 + * 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. - * - * $FreeBSD$ */ + +/* +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 -#include - -#include -#include +#include -#include "syscon_if.h" -#include "iicbus_if.h" #include "anx_dp.h" +#include "iicbus_if.h" +#if ANXDP_AUDIO +#include +#endif -#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)) +#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 -static void anxdp_init_aux(struct anxdp_softc * sc); +#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) -static void edp_connector_destroy(struct drm_connector *connector); -static void anxdp_bringup(struct anxdp_softc * const sc); +struct anxdp_link { + uint8_t revision; + u_int rate; + u_int num_lanes; + bool enhanced_framing; +}; -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); +#if ANXDP_AUDIO +enum anxdp_dai_mixer_ctrl { + ANXDP_DAI_OUTPUT_CLASS, + ANXDP_DAI_INPUT_CLASS, -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, + ANXDP_DAI_OUTPUT_MASTER_VOLUME, + ANXDP_DAI_INPUT_DAC_VOLUME, + ANXDP_DAI_MIXER_CTRL_LAST }; -static int anxdp_await_pll_lock(struct anxdp_softc * const sc) +static void +anxdp_audio_init(struct anxdp_softc *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; - } +#endif - -#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 inline const bool +isrockchip(struct anxdp_softc * const sc) +{ + return (sc->sc_flags & ANXDP_FLAG_ROCKCHIP) != 0; +} static enum drm_connector_status -edp_connector_detect(struct drm_connector *connector, bool force) +anxdp_connector_detect(struct drm_connector *connector, bool force) { #if 0 struct anxdp_connector *anxdp_connector = to_anxdp_connector(connector); @@ -119,162 +271,181 @@ edp_connector_detect(struct drm_connector *connector, bool force) return connector_status_connected; } - -static int edp_connector_get_modes(struct drm_connector *connector) +static void +anxdp_connector_destroy(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; + drm_connector_unregister(connector); + drm_connector_cleanup(connector); } - -static const struct drm_connector_funcs edp_connector_funcs = { - .detect = edp_connector_detect, +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 = edp_connector_destroy, + .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, - // .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) + +static void +anxdp_analog_power_up_all(struct anxdp_softc * const sc) { - sc->sc_bridge.funcs = &edp_bridge_funcs; - drm_bridge_attach(encoder, &sc->sc_bridge, NULL,0); -} + 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 -edp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) +anxdp_await_pll_lock(struct anxdp_softc * const sc) { - struct anxdp_softc *sc; - sc = container_of(bridge, struct anxdp_softc, sc_bridge); + u_int timeout; + + for (timeout = 0; timeout < 100; timeout++) { + if ((ANXDP_READ(sc, ANXDP_DEBUG_CTL) & PLL_LOCK) != 0) + return 0; + DELAY(20); + } - sc->sc_connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - sc->sc_connector.interlace_allowed = 0; - sc->sc_connector.doublescan_allowed = 0; + return ETIMEDOUT; +} - drm_connector_helper_add(&sc->sc_connector, - &edp_connector_helper_funcs); +static void +anxdp_init_hpd(struct anxdp_softc * const sc) +{ + uint32_t sc3; - drm_connector_init_with_ddc(bridge->dev,&sc->sc_connector, - &edp_connector_funcs, DRM_MODE_CONNECTOR_eDP,&sc->sc_dpaux.ddc); + ANXDP_WRITE(sc, ANXDP_COMMON_INT_STA_4, 0x7); + ANXDP_WRITE(sc, ANXDP_DP_INT_STA, INT_HPD); - drm_connector_attach_encoder(&sc->sc_connector, &sc->sc_encoder); + sc3 = ANXDP_READ(sc, ANXDP_SYS_CTL_3); + sc3 &= ~(F_HPD | HPD_CTRL); + ANXDP_WRITE(sc, ANXDP_SYS_CTL_3, sc3); - return (0); + 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); -static void -edp_bridge_enable(struct drm_bridge *bridge) -{ + pd = ANXDP_READ(sc, pd_reg); + pd &= ~pd_mask; + ANXDP_WRITE(sc, pd_reg, pd); - struct anxdp_softc * sc = container_of(bridge,struct anxdp_softc,sc_bridge); + fe2 = ANXDP_READ(sc, ANXDP_FUNC_EN_2); + fe2 |= AUX_FUNC_EN_N; + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, fe2); - uint32_t val; + 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); - 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); + ANXDP_WRITE(sc, ANXDP_AUX_CH_DEFER_CTL, + DEFER_CTRL_EN | DEFER_COUNT(1)); - 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); + fe2 = ANXDP_READ(sc, ANXDP_FUNC_EN_2); + fe2 &= ~AUX_FUNC_EN_N; + ANXDP_WRITE(sc, ANXDP_FUNC_EN_2, fe2); +} - ANXDP_WRITE(sc, ANXDP_SOC_GENERAL_CTL, - AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE); - edp_train_link(sc); +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; - val = ANXDP_READ(sc,ANXDP_VIDEO_CTL_1); - val |= VIDEO_EN; - ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + 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; + } - 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 - } + drm_connector_update_edid_property(connector, pedid); + if (pedid == NULL) + return 0; + error = drm_add_edid_modes(connector, pedid); -static void -edp_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} + if (pedid != NULL) + kfree(pedid); -static void edp_bridge_pre_enable(struct drm_bridge *bridge) -{ + return error; } -static void -edp_bridge_disable(struct drm_bridge *bridge) +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 void edp_bridge_post_disable(struct drm_bridge *bridge) +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; -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); + connector->polled = + DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; - memcpy(&sc->sc_curmode, mode, sizeof(struct drm_display_mode)); + 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 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) +static void +anxdp_macro_reset(struct anxdp_softc * const sc) { uint32_t val; - val = ANXDP_READ(sc,ANXDP_PHY_TEST); + val = ANXDP_READ(sc, ANXDP_PHY_TEST); val |= MACRO_RST; ANXDP_WRITE(sc, ANXDP_PHY_TEST, val); DELAY(10); @@ -283,26 +454,25 @@ static void edp_macro_reset(struct anxdp_softc * const sc) } static void -edp_link_start(struct anxdp_softc * const sc, struct drm_dp_link * const link) +anxdp_link_start(struct anxdp_softc * const sc, struct anxdp_link * const link) { uint8_t training[4]; + uint8_t bw[2]; 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; - + 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); - err = drm_dp_dpcd_write(&sc->sc_dpaux, DP_LINK_BW_SET, values, sizeof(values)); - if (err < 0) { + 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, @@ -314,13 +484,12 @@ edp_link_start(struct anxdp_softc * const sc, struct drm_dp_link * const link) } if (anxdp_await_pll_lock(sc) != 0) { - printf("PLL lock timeout\n"); + 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; + DP_TRAIN_VOLTAGE_SWING_LEVEL_0; } drm_dp_dpcd_write(&sc->sc_dpaux, DP_TRAINING_LANE0_SET, training, @@ -328,7 +497,8 @@ edp_link_start(struct anxdp_softc * const sc, struct drm_dp_link * const link) } static void -edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_link * const link) +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]; @@ -340,11 +510,12 @@ edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_link * c DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1); tries = 0; - again: +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)) { @@ -354,6 +525,7 @@ edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_link * c goto cr_fail; } + wakeup(sc->sc_dev); return; cr_fail: @@ -363,6 +535,7 @@ edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_link * c 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]); @@ -373,7 +546,7 @@ edp_process_clock_recovery(struct anxdp_softc * const sc, struct drm_dp_link * c } static void -edp_process_eq(struct anxdp_softc * const sc, struct drm_dp_link * const link) +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]; @@ -416,107 +589,365 @@ edp_process_eq(struct anxdp_softc * const sc, struct drm_dp_link * const link) link->num_lanes); goto again; } + static void -edp_train_link(struct anxdp_softc * const sc) +anxdp_train_link(struct anxdp_softc * const sc) { - int err; - u8 values[3]; - u8 value; - struct drm_dp_link link; + struct anxdp_link link; + uint8_t values[3], power; + int ret; - edp_macro_reset(sc); - drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, values, sizeof(values)); + 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.capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; - + 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); + } - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ - if (link.revision < 0x11) + if (DP_RECEIVER_CAP_SIZE != drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, + sc->sc_dpcd, DP_RECEIVER_CAP_SIZE)) return; - err = drm_dp_dpcd_readb(&sc->sc_dpaux, DP_SET_POWER, &value); - if (err <0) - return; + anxdp_link_start(sc, &link); + anxdp_process_clock_recovery(sc, &link); + anxdp_process_eq(sc, &link); - value &= ~DP_SET_POWER_MASK; - value |= DP_SET_POWER_D0; + ANXDP_WRITE(sc, ANXDP_TRAINING_PTN_SET, 0); + drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); - drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_SET_POWER, value); +} +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); - /* - * 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); + val = ANXDP_READ(sc, ANXDP_VIDEO_CTL_1); + val &= ~VIDEO_MUTE; + ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); - if (DP_RECEIVER_CAP_SIZE != drm_dp_dpcd_read(&sc->sc_dpaux, DP_DPCD_REV, - sc->sc_dpcd, DP_RECEIVER_CAP_SIZE)) - return; + 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); - edp_link_start(sc, &link); - edp_process_clock_recovery(sc, &link); - edp_process_eq(sc, &link); + 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); - ANXDP_WRITE(sc, ANXDP_TRAINING_PTN_SET, 0); - drm_dp_dpcd_writeb(&sc->sc_dpaux, DP_TRAINING_PATTERN_SET, - DP_TRAINING_PATTERN_DISABLE); + 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_init_hpd(struct anxdp_softc * const sc) +anxdp_bridge_enable(struct drm_bridge *bridge) { - uint32_t sc3; - ANXDP_WRITE(sc, ANXDP_COMMON_INT_STA_4, 0x7); - ANXDP_WRITE(sc, ANXDP_DP_INT_STA, INT_HPD); + struct anxdp_softc * const sc = bridge->driver_private; + uint32_t val; - sc3 = ANXDP_READ(sc,ANXDP_SYS_CTL_3); - sc3 &= ~(F_HPD | HPD_CTRL); - ANXDP_WRITE(sc, ANXDP_SYS_CTL_3, sc3); + 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); - sc3 = ANXDP_READ(sc,ANXDP_SYS_CTL_3); - sc3 |= F_HPD | HPD_CTRL; - ANXDP_WRITE(sc, ANXDP_SYS_CTL_3, sc3); + 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_analog_power_up_all(struct anxdp_softc * const sc) +anxdp_bridge_pre_enable(struct drm_bridge *bridge) { - 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 void +anxdp_bridge_disable(struct drm_bridge *bridge) +{ } +static void +anxdp_bridge_post_disable(struct drm_bridge *bridge) +{ +} -static inline const bool -isrockchip(struct anxdp_softc * const sc) +static void +anxdp_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { - return (sc->sc_flags & ANXDP_FLAG_ROCKCHIP) != 0; + 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); + 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); + 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; @@ -545,8 +976,7 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) if (!(dpmsg->request & DP_AUX_I2C_READ)) { for (i = 0; i < dpmsg->size; i++) { - ANXDP_WRITE(sc, - ANXDP_BUF_DATA(i), + ANXDP_WRITE(sc, ANXDP_BUF_DATA(i), ((const uint8_t *)(dpmsg->buffer))[i]); ret++; } @@ -557,7 +987,7 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) AUX_EN | ((dpmsg->size == 0) ? ADDR_ONLY : 0)); loop_timeout = 0; - val = ANXDP_READ(sc,ANXDP_AUX_CH_CTL_2); + val = ANXDP_READ(sc, ANXDP_AUX_CH_CTL_2); while ((val & AUX_EN) != 0) { if (++loop_timeout > 20000) { ret = -ETIMEDOUT; @@ -569,7 +999,7 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) } loop_timeout = 0; - val = ANXDP_READ(sc,ANXDP_DP_INT_STA); + val = ANXDP_READ(sc, ANXDP_DP_INT_STA); while (!(val & RPLY_RECEIV)) { if (++loop_timeout > 2000) { ret = -ETIMEDOUT; @@ -583,7 +1013,7 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) ANXDP_WRITE(sc, ANXDP_DP_INT_STA, RPLY_RECEIV); - val = ANXDP_READ(sc,ANXDP_DP_INT_STA); + val = ANXDP_READ(sc, ANXDP_DP_INT_STA); if ((val & AUX_ERR) != 0) { ANXDP_WRITE(sc, ANXDP_DP_INT_STA, AUX_ERR); @@ -591,7 +1021,7 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) goto out; } - val = ANXDP_READ(sc,ANXDP_AUX_CH_STA); + val = ANXDP_READ(sc, ANXDP_AUX_CH_STA); if (AUX_STATUS(val) != 0) { ret = -EREMOTEIO; goto out; @@ -606,160 +1036,189 @@ anxdp_dp_aux_transfer(struct drm_dp_aux *dpaux, struct drm_dp_aux_msg *dpmsg) } } - val = ANXDP_READ(sc,ANXDP_AUX_RX_COMM); + 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->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->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; + return ret; } + int anxdp_attach(struct anxdp_softc *sc) { - phandle_t node; - node = ofw_bus_get_node(sc->sc_dev); +#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; - sc->sc_dpaux.transfer=anxdp_dp_aux_transfer; + 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"); } - 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) + +int +anxdp_bind(struct anxdp_softc *sc, struct drm_encoder *encoder) { - uint32_t val; + int error; - val = ANXDP_READ(sc,ANXDP_VIDEO_CTL_1); - val &= ~VIDEO_EN; - ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + sc->sc_connector.encoder = encoder; - val = ANXDP_READ(sc,ANXDP_VIDEO_CTL_1); - val &= ~VIDEO_MUTE; - ANXDP_WRITE(sc, ANXDP_VIDEO_CTL_1, val); + 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; - 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); + if (sc->sc_panel != NULL && + sc->sc_panel->funcs != NULL && + sc->sc_panel->funcs->prepare != NULL) + sc->sc_panel->funcs->prepare(sc->sc_panel); - 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); + return 0; +} - 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"); +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); + } } - 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); +out: + return 0; } -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[] = { + 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 index 3c90bff..351b253 100644 --- a/bridges/anxdp/anx_dp.h +++ b/bridges/anxdp/anx_dp.h @@ -1,10 +1,11 @@ + +/* $NetBSD: anx_dp.h,v 1.5 2021/12/19 12:43:37 riastradh Exp $ */ + /*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021 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: @@ -14,224 +15,76 @@ * 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 + * 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. - * - * $FreeBSD$ */ +#ifndef _DEV_IC_ANXDP_H +#include +__FBSDID("$FreeBSD$"); + +#define ANXDP_AUDIO 0 +#define _DEV_IC_ANXDP_H + +#if ANXDP_AUDIO +#include +#endif -#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 +#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 { - struct mtx mtx; - device_t iicbus; - struct device* sc_dev; - + device_t 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; + 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; + struct drm_panel *sc_panel; uint8_t sc_dpcd[DP_RECEIVER_CAP_SIZE]; - struct drm_bridge sc_bridge; + device_t iicbus; + struct i2c_adapter *ddc; 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); +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 /* ANXEDP_H */ - +#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/drm_dp_mst_topology.c b/core/drm_dp_mst_topology.c index 6c88bd3..1e26b89 100644 --- a/core/drm_dp_mst_topology.c +++ b/core/drm_dp_mst_topology.c @@ -27,9 +27,7 @@ #include #include #include -#ifdef __linux__ #include -#endif #if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) #include @@ -2118,11 +2116,9 @@ 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); } @@ -2140,11 +2136,8 @@ 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); @@ -4431,23 +4424,10 @@ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) */ const int timeout_ms = 3000; int ret, status; -#ifdef __FreeBSD__ - int tries=0; - status = do_get_act_status(mgr->aux); - while(!(status & DP_PAYLOAD_ACT_HANDLED || status < 0)) { - DELAY(200); - status = do_get_act_status(mgr->aux); - tries++; - if(tries >=15000) - ret = -ETIMEDOUT; - - } -#elif defined( __linux__) ret = readx_poll_timeout(do_get_act_status, mgr->aux, status, status & DP_PAYLOAD_ACT_HANDLED || status < 0, 200, timeout_ms * USEC_PER_MSEC); -#endif if (ret < 0 && status >= 0) { DRM_ERROR("Failed to get ACT after %dms, last status: %02x\n", timeout_ms, status); @@ -5379,9 +5359,7 @@ 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 ea9b8a7..b937895 100644 --- a/core/include/drm/drm_dp_helper.h +++ b/core/include/drm/drm_dp_helper.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/drmkpi/include/linux/i2c.h b/drmkpi/include/linux/i2c.h index 67103a7..432981d 100644 --- a/drmkpi/include/linux/i2c.h +++ b/drmkpi/include/linux/i2c.h @@ -35,20 +35,11 @@ #include #include #include -#include + /* 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 0x0004 -/* No need for us */ -#define I2C_FUNC_I2C 0 -#define I2C_FUNC_SMBUS_EMUL 0 -#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0 -#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0 -#define I2C_FUNC_10BIT_ADDR 0 - -#define I2C_CLASS_DDC 0x8 struct i2c_msg { uint16_t addr; @@ -61,35 +52,15 @@ struct i2c_adapter { device_t bsddev; char name[20]; - const struct i2c_lock_operations *lock_ops; - const struct i2c_algorithm *algo; - void *algo_data; - int retries; - struct module *owner; - unsigned int class; /* I2C_CLASS_* */ - struct { - device_t parent; - } dev; }; -struct i2c_algorithm { - int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int); - uint32_t (*functionality)(struct i2c_adapter *); -}; - -struct i2c_lock_operations { - void (*lock_bus)(struct i2c_adapter *, unsigned int); - int (*trylock_bus)(struct i2c_adapter *, unsigned int); - void (*unlock_bus)(struct i2c_adapter *, unsigned int); -}; - 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 | M_ZERO); + adap = malloc(sizeof(struct i2c_adapter), M_TEMP, M_WAITOK); adap->bsddev = dev; strcpy(adap->name, "emulated-i2c"); return (adap); @@ -98,57 +69,24 @@ i2c_bsd_adapter(device_t dev) static inline int i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - - int ret,retries = 0; - retries = adap->retries == 0 ? 1 : adap->retries; - - if (adap->lock_ops) - adap->lock_ops->lock_bus(adap, 0); - if (adap->algo != NULL && adap->algo->master_xfer != NULL) { - for (; retries != 0; retries--) { - ret = adap->algo->master_xfer(adap, msgs, num); - if (ret != -EAGAIN) - break; - } - - } else { - struct iic_msg *bsd_msgs; - int i; - - bsd_msgs = malloc(sizeof(struct iic_msg) * num, M_TEMP, M_WAITOK); - memcpy(bsd_msgs, msgs, sizeof(struct iic_msg) * num); - /*inux uses 7-bit addresses but FreeBSD 8-bit */ - for (i = 0; i < num; i++) { - bsd_msgs[i].slave = msgs[i].addr << 1; - bsd_msgs[i].flags = msgs[i].flags; - 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) - ret = (-ret); + 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 */ + for (i = 0; i < num; i++) { + bsd_msgs[i].slave = msgs[i].addr << 1; + bsd_msgs[i].flags = msgs[i].flags; + bsd_msgs[i].len = msgs[i].len; + bsd_msgs[i].buf = msgs[i].buf; } - if (adap->lock_ops) - adap->lock_ops->unlock_bus(adap, 0); - if (ret < 0) - return (ret); - - return (num); - - -} -static inline int -i2c_add_adapter(struct i2c_adapter *adapter __unused) -{ - - return 0; -} - -static inline void -i2c_del_adapter(struct i2c_adapter *adapter __unused) -{ + + ret = iicbus_transfer(adap->bsddev, bsd_msgs, num); + free(bsd_msgs, M_TEMP); + if (ret != 0) + return (-ret); + return (num); } #endif /* __DRMCOMPAT_LINUX_I2C_H__ */ diff --git a/extra_patches/files b/extra_patches/files index 8ba547a..b6549a0 100644 --- a/extra_patches/files +++ b/extra_patches/files @@ -15,7 +15,7 @@ dev/drm/core/drm_connector.c optional compat_drmcompat drm compile-with "${DRM 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_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 ad70a92..a711ab0 100644 --- a/extra_patches/files.bridges +++ b/extra_patches/files.bridges @@ -2,10 +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 | rk_edp +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_edp compile-with "${DRM_C}" +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 31c915a..95f67a8 100644 --- a/extra_patches/files.rk +++ b/extra_patches/files.rk @@ -4,4 +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}" +dev/drm/rockchip/rk_anxdp.c optional drm fdt rk_anxdp compile-with "${DRM_C}" diff --git a/rockchip/rk_edp.c b/rockchip/rk_anxdp.c similarity index 52% rename from rockchip/rk_edp.c rename to rockchip/rk_anxdp.c index f6c0787..4c49d95 100644 --- a/rockchip/rk_edp.c +++ b/rockchip/rk_anxdp.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Jesper Schmitz Mouridsen + * Copyright (c) 2022 Jesper Schmitz Mouridsen * Copyright (c) 2019 Jonathan A. Kollasch * Redistribution and use in source and binary forms, with or without @@ -27,14 +27,39 @@ * * $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 -__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) @@ -45,64 +70,80 @@ static struct ofw_compat_data rkedp_compat_data[] = { }; -static struct resource_spec rk_edp_spec[] = { +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_edp_probe(device_t dev); -static int rk_edp_attach(device_t dev); +static int rk_anxdp_probe(device_t dev); +static int rk_anxdp_attach(device_t dev); -static int rk_edp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm); +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_edp_softc, sc_base) +#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 void rk_anxdp_select_input(struct rk_edp_softc *sc, u_int crtc_index) +static bool +rk_anxdp_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - 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); + 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) +rk_anxdp_encoder_prepare(struct drm_encoder *encoder,struct drm_atomic_state * state) { - 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); + 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)); - 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, + .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_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) +#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_edp_methods[] = { - DEVMETHOD(device_probe, rk_edp_probe), - DEVMETHOD(device_attach, rk_edp_attach), - DEVMETHOD(dw_hdmi_add_encoder, rk_edp_add_encoder), +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_edp_probe(device_t dev){ +rk_anxdp_probe(device_t dev){ if (!ofw_bus_status_okay(dev)) return (ENXIO); @@ -110,21 +151,21 @@ rk_edp_probe(device_t dev){ if (ofw_bus_search_compatible(dev, rkedp_compat_data)->ocd_data == 0) return (ENXIO); - device_set_desc(dev, "RockChip edp"); + device_set_desc(dev, "RockChip Analogix DP"); return (BUS_PROBE_DEFAULT); } static int -rk_edp_attach(device_t dev) +rk_anxdp_attach(device_t dev) { - struct rk_edp_softc *sc; + struct rk_anxdp_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) { + if (bus_alloc_resources(dev,rk_anxdp_spec, sc->sc_base.res)!=0) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } @@ -169,7 +210,7 @@ rk_edp_attach(device_t dev) } 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); @@ -177,24 +218,28 @@ rk_edp_attach(device_t dev) } static int -rk_edp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm) +rk_anxdp_add_encoder(device_t dev, struct drm_crtc *crtc, struct drm_device *drm) { - struct rk_edp_softc *sc; + struct rk_anxdp_softc *sc; + int error = 0; 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_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); - rk_anxdp_select_input(sc,crtc->index); - anxdp_add_bridge(&sc->sc_base,&sc->sc_base.sc_encoder); - return (0); + sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP; + + error = anxdp_bind(&sc->sc_base, &sc->sc_encoder); + + return error; } -static devclass_t rk_edp_devclass; -DEFINE_CLASS_1(rk_edp, rk_edp_driver, rk_edp_methods, - sizeof(struct rk_edp_softc), anxdp_driver); +DEFINE_CLASS_1(rk_anxdp, rk_anxdp_driver, rk_anxdp_methods, + sizeof(struct rk_anxdp_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); +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_edp.h b/rockchip/rk_edp.h deleted file mode 100644 index 30139ae..0000000 --- a/rockchip/rk_edp.h +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * 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 6fee9b2..048c4c2 100644 --- a/rockchip/rk_vop.c +++ b/rockchip/rk_vop.c @@ -585,13 +585,13 @@ rk_vop_add_encoder(struct rk_vop_softc *sc, struct drm_device *drm) node = ofw_bus_get_node(sc->dev); if (node == 0) return (ENOENT); - for(i=1;i<=2;i++) { + 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) { - sc->connector_type = strncmp(device_get_name(dev),"rk_edp",6)==0 ? DRM_MODE_CONNECTOR_eDP : DRM_MODE_CONNECTOR_HDMIA; - + 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; @@ -646,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);