Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Enric Balletbò i Serra | 5381 | 93.16% | 1 | 3.45% |
Brian Masney | 159 | 2.75% | 4 | 13.79% |
Thierry Reding | 89 | 1.54% | 1 | 3.45% |
Stephen Chandler Paul | 78 | 1.35% | 4 | 13.79% |
Laurent Pinchart | 20 | 0.35% | 3 | 10.34% |
Hai Li | 9 | 0.16% | 1 | 3.45% |
Jose Abreu | 8 | 0.14% | 1 | 3.45% |
Sam Ravnborg | 5 | 0.09% | 1 | 3.45% |
Ville Syrjälä | 5 | 0.09% | 1 | 3.45% |
Thomas Zimmermann | 4 | 0.07% | 1 | 3.45% |
Alicja Michalska | 3 | 0.05% | 1 | 3.45% |
Torsten Duwe | 3 | 0.05% | 1 | 3.45% |
Daniel Vetter | 3 | 0.05% | 2 | 6.90% |
Thomas Gleixner | 2 | 0.03% | 1 | 3.45% |
Boris Brezillon | 2 | 0.03% | 1 | 3.45% |
Uwe Kleine-König | 2 | 0.03% | 2 | 6.90% |
Icenowy Zheng | 1 | 0.02% | 1 | 3.45% |
Marek Vašut | 1 | 0.02% | 1 | 3.45% |
Shashank Sharma | 1 | 0.02% | 1 | 3.45% |
Total | 5776 | 29 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2016, Analogix Semiconductor. * * Based on anx7808 driver obtained from chromeos with copyright: * Copyright(c) 2013, Google Inc. */ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include "analogix-anx78xx.h" #define I2C_NUM_ADDRESSES 5 #define I2C_IDX_TX_P0 0 #define I2C_IDX_TX_P1 1 #define I2C_IDX_TX_P2 2 #define I2C_IDX_RX_P0 3 #define I2C_IDX_RX_P1 4 #define XTAL_CLK 270 /* 27M */ static const u8 anx7808_i2c_addresses[] = { [I2C_IDX_TX_P0] = 0x78, [I2C_IDX_TX_P1] = 0x7a, [I2C_IDX_TX_P2] = 0x72, [I2C_IDX_RX_P0] = 0x7e, [I2C_IDX_RX_P1] = 0x80, }; static const u8 anx781x_i2c_addresses[] = { [I2C_IDX_TX_P0] = 0x70, [I2C_IDX_TX_P1] = 0x7a, [I2C_IDX_TX_P2] = 0x72, [I2C_IDX_RX_P0] = 0x7e, [I2C_IDX_RX_P1] = 0x80, }; struct anx78xx_platform_data { struct regulator *dvdd10; struct gpio_desc *gpiod_hpd; struct gpio_desc *gpiod_pd; struct gpio_desc *gpiod_reset; int hpd_irq; int intp_irq; }; struct anx78xx { struct drm_dp_aux aux; struct drm_bridge bridge; struct i2c_client *client; struct edid *edid; struct drm_connector connector; struct anx78xx_platform_data pdata; struct mutex lock; /* * I2C Slave addresses of ANX7814 are mapped as TX_P0, TX_P1, TX_P2, * RX_P0 and RX_P1. */ struct i2c_client *i2c_dummy[I2C_NUM_ADDRESSES]; struct regmap *map[I2C_NUM_ADDRESSES]; u16 chipid; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool powered; }; static inline struct anx78xx *connector_to_anx78xx(struct drm_connector *c) { return container_of(c, struct anx78xx, connector); } static inline struct anx78xx *bridge_to_anx78xx(struct drm_bridge *bridge) { return container_of(bridge, struct anx78xx, bridge); } static int anx78xx_set_bits(struct regmap *map, u8 reg, u8 mask) { return regmap_update_bits(map, reg, mask, mask); } static int anx78xx_clear_bits(struct regmap *map, u8 reg, u8 mask) { return regmap_update_bits(map, reg, mask, 0); } static ssize_t anx78xx_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { struct anx78xx *anx78xx = container_of(aux, struct anx78xx, aux); return anx_dp_aux_transfer(anx78xx->map[I2C_IDX_TX_P0], msg); } static int anx78xx_set_hpd(struct anx78xx *anx78xx) { int err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_RX_P0], SP_TMDS_CTRL_BASE + 7, SP_PD_RT); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL3_REG, SP_HPD_OUT); if (err) return err; return 0; } static int anx78xx_clear_hpd(struct anx78xx *anx78xx) { int err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL3_REG, SP_HPD_OUT); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_TMDS_CTRL_BASE + 7, SP_PD_RT); if (err) return err; return 0; } static const struct reg_sequence tmds_phy_initialization[] = { { SP_TMDS_CTRL_BASE + 1, 0x90 }, { SP_TMDS_CTRL_BASE + 2, 0xa9 }, { SP_TMDS_CTRL_BASE + 6, 0x92 }, { SP_TMDS_CTRL_BASE + 7, 0x80 }, { SP_TMDS_CTRL_BASE + 20, 0xf2 }, { SP_TMDS_CTRL_BASE + 22, 0xc4 }, { SP_TMDS_CTRL_BASE + 23, 0x18 }, }; static int anx78xx_rx_initialization(struct anx78xx *anx78xx) { int err; err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_MUTE_CTRL_REG, SP_AUD_MUTE | SP_VID_MUTE); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_CHIP_CTRL_REG, SP_MAN_HDMI5V_DET | SP_PLLLOCK_CKDT_EN | SP_DIGITAL_CKDT_EN); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_SOFTWARE_RESET1_REG, SP_HDCP_MAN_RST | SP_SW_MAN_RST | SP_TMDS_RST | SP_VIDEO_RST); if (err) return err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_RX_P0], SP_SOFTWARE_RESET1_REG, SP_HDCP_MAN_RST | SP_SW_MAN_RST | SP_TMDS_RST | SP_VIDEO_RST); if (err) return err; /* Sync detect change, GP set mute */ err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_AUD_EXCEPTION_ENABLE_BASE + 1, BIT(5) | BIT(6)); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_AUD_EXCEPTION_ENABLE_BASE + 3, SP_AEC_EN21); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_AUDVID_CTRL_REG, SP_AVC_EN | SP_AAC_OE | SP_AAC_EN); if (err) return err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_RX_P0], SP_SYSTEM_POWER_DOWN1_REG, SP_PWDN_CTRL); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_RX_P0], SP_VID_DATA_RANGE_CTRL_REG, SP_R2Y_INPUT_LIMIT); if (err) return err; /* Enable DDC stretch */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_EXTRA_I2C_DEV_ADDR_REG, SP_I2C_EXTRA_ADDR); if (err) return err; /* TMDS phy initialization */ err = regmap_multi_reg_write(anx78xx->map[I2C_IDX_RX_P0], tmds_phy_initialization, ARRAY_SIZE(tmds_phy_initialization)); if (err) return err; err = anx78xx_clear_hpd(anx78xx); if (err) return err; return 0; } static const u8 dp_tx_output_precise_tune_bits[20] = { 0x01, 0x03, 0x07, 0x7f, 0x71, 0x6b, 0x7f, 0x73, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x42, 0x1e, 0x3e, 0x72, 0x7e, }; static int anx78xx_link_phy_initialization(struct anx78xx *anx78xx) { int err; /* * REVISIT : It is writing to a RESERVED bits in Analog Control 0 * register. */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_ANALOG_CTRL0_REG, 0x02); if (err) return err; /* * Write DP TX output emphasis precise tune bits. */ err = regmap_bulk_write(anx78xx->map[I2C_IDX_TX_P1], SP_DP_TX_LT_CTRL0_REG, dp_tx_output_precise_tune_bits, ARRAY_SIZE(dp_tx_output_precise_tune_bits)); if (err) return err; return 0; } static int anx78xx_xtal_clk_sel(struct anx78xx *anx78xx) { unsigned int value; int err; err = regmap_update_bits(anx78xx->map[I2C_IDX_TX_P2], SP_ANALOG_DEBUG2_REG, SP_XTAL_FRQ | SP_FORCE_SW_OFF_BYPASS, SP_XTAL_FRQ_27M); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL3_REG, XTAL_CLK & SP_WAIT_COUNTER_7_0_MASK); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL4_REG, ((XTAL_CLK & 0xff00) >> 2) | (XTAL_CLK / 10)); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_I2C_GEN_10US_TIMER0_REG, XTAL_CLK & 0xff); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_I2C_GEN_10US_TIMER1_REG, (XTAL_CLK & 0xff00) >> 8); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_AUX_MISC_CTRL_REG, XTAL_CLK / 10 - 1); if (err) return err; err = regmap_read(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_US_TIMER_CTRL_REG, &value); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_US_TIMER_CTRL_REG, (value & SP_MS_TIMER_MARGIN_10_8_MASK) | ((((XTAL_CLK / 10) >> 1) - 2) << 3)); if (err) return err; return 0; } static const struct reg_sequence otp_key_protect[] = { { SP_OTP_KEY_PROTECT1_REG, SP_OTP_PSW1 }, { SP_OTP_KEY_PROTECT2_REG, SP_OTP_PSW2 }, { SP_OTP_KEY_PROTECT3_REG, SP_OTP_PSW3 }, }; static int anx78xx_tx_initialization(struct anx78xx *anx78xx) { int err; /* Set terminal resistor to 50 ohm */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL2_REG, 0x30); if (err) return err; /* Enable aux double diff output */ err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL2_REG, 0x08); if (err) return err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_HDCP_CTRL_REG, SP_AUTO_EN | SP_AUTO_START); if (err) return err; err = regmap_multi_reg_write(anx78xx->map[I2C_IDX_TX_P0], otp_key_protect, ARRAY_SIZE(otp_key_protect)); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_HDCP_KEY_COMMAND_REG, SP_DISABLE_SYNC_HDCP); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL8_REG, SP_VID_VRES_TH); if (err) return err; /* * DP HDCP auto authentication wait timer (when downstream starts to * auth, DP side will wait for this period then do auth automatically) */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_HDCP_AUTO_TIMER_REG, 0x00); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_HDCP_CTRL_REG, SP_LINK_POLLING); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_LINK_DEBUG_CTRL_REG, SP_M_VID_DEBUG); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_ANALOG_DEBUG2_REG, SP_POWERON_TIME_1P5MS); if (err) return err; err = anx78xx_xtal_clk_sel(anx78xx); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_AUX_DEFER_CTRL_REG, SP_DEFER_CTRL_EN | 0x0c); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_POLLING_CTRL_REG, SP_AUTO_POLLING_DISABLE); if (err) return err; /* * Short the link integrity check timer to speed up bstatus * polling for HDCP CTS item 1A-07 */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_HDCP_LINK_CHECK_TIMER_REG, 0x1d); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_MISC_CTRL_REG, SP_EQ_TRAINING_LOOP); if (err) return err; /* Power down the main link by default */ err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_ANALOG_POWER_DOWN_REG, SP_CH0_PD); if (err) return err; err = anx78xx_link_phy_initialization(anx78xx); if (err) return err; /* Gen m_clk with downspreading */ err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_M_CALCULATION_CTRL_REG, SP_M_GEN_CLK_SEL); if (err) return err; return 0; } static int anx78xx_enable_interrupts(struct anx78xx *anx78xx) { int err; /* * BIT0: INT pin assertion polarity: 1 = assert high * BIT1: INT pin output type: 0 = push/pull */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_INT_CTRL_REG, 0x01); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_COMMON_INT_MASK4_REG, SP_HPD_LOST | SP_HPD_PLUG); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_DP_INT_MASK1_REG, SP_TRAINING_FINISH); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_INT_MASK1_REG, SP_CKDT_CHG | SP_SCDT_CHG); if (err) return err; return 0; } static void anx78xx_poweron(struct anx78xx *anx78xx) { struct anx78xx_platform_data *pdata = &anx78xx->pdata; int err; if (WARN_ON(anx78xx->powered)) return; if (pdata->dvdd10) { err = regulator_enable(pdata->dvdd10); if (err) { DRM_ERROR("Failed to enable DVDD10 regulator: %d\n", err); return; } usleep_range(1000, 2000); } gpiod_set_value_cansleep(pdata->gpiod_reset, 1); usleep_range(1000, 2000); gpiod_set_value_cansleep(pdata->gpiod_pd, 0); usleep_range(1000, 2000); gpiod_set_value_cansleep(pdata->gpiod_reset, 0); /* Power on registers module */ anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_POWERDOWN_CTRL_REG, SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD); anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_POWERDOWN_CTRL_REG, SP_REGISTER_PD | SP_TOTAL_PD); anx78xx->powered = true; } static void anx78xx_poweroff(struct anx78xx *anx78xx) { struct anx78xx_platform_data *pdata = &anx78xx->pdata; int err; if (WARN_ON(!anx78xx->powered)) return; gpiod_set_value_cansleep(pdata->gpiod_reset, 1); usleep_range(1000, 2000); gpiod_set_value_cansleep(pdata->gpiod_pd, 1); usleep_range(1000, 2000); if (pdata->dvdd10) { err = regulator_disable(pdata->dvdd10); if (err) { DRM_ERROR("Failed to disable DVDD10 regulator: %d\n", err); return; } usleep_range(1000, 2000); } anx78xx->powered = false; } static int anx78xx_start(struct anx78xx *anx78xx) { int err; /* Power on all modules */ err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_POWERDOWN_CTRL_REG, SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD); err = anx78xx_enable_interrupts(anx78xx); if (err) { DRM_ERROR("Failed to enable interrupts: %d\n", err); goto err_poweroff; } err = anx78xx_rx_initialization(anx78xx); if (err) { DRM_ERROR("Failed receiver initialization: %d\n", err); goto err_poweroff; } err = anx78xx_tx_initialization(anx78xx); if (err) { DRM_ERROR("Failed transmitter initialization: %d\n", err); goto err_poweroff; } /* * This delay seems to help keep the hardware in a good state. Without * it, there are times where it fails silently. */ usleep_range(10000, 15000); return 0; err_poweroff: DRM_ERROR("Failed SlimPort transmitter initialization: %d\n", err); anx78xx_poweroff(anx78xx); return err; } static int anx78xx_init_pdata(struct anx78xx *anx78xx) { struct anx78xx_platform_data *pdata = &anx78xx->pdata; struct device *dev = &anx78xx->client->dev; /* 1.0V digital core power regulator */ pdata->dvdd10 = devm_regulator_get(dev, "dvdd10"); if (IS_ERR(pdata->dvdd10)) { if (PTR_ERR(pdata->dvdd10) != -EPROBE_DEFER) DRM_ERROR("DVDD10 regulator not found\n"); return PTR_ERR(pdata->dvdd10); } /* GPIO for HPD */ pdata->gpiod_hpd = devm_gpiod_get(dev, "hpd", GPIOD_IN); if (IS_ERR(pdata->gpiod_hpd)) return PTR_ERR(pdata->gpiod_hpd); /* GPIO for chip power down */ pdata->gpiod_pd = devm_gpiod_get(dev, "pd", GPIOD_OUT_HIGH); if (IS_ERR(pdata->gpiod_pd)) return PTR_ERR(pdata->gpiod_pd); /* GPIO for chip reset */ pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); return PTR_ERR_OR_ZERO(pdata->gpiod_reset); } static int anx78xx_dp_link_training(struct anx78xx *anx78xx) { u8 dp_bw, dpcd[2]; int err; err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_MUTE_CTRL_REG, 0x0); if (err) return err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_POWERDOWN_CTRL_REG, SP_TOTAL_PD); if (err) return err; err = drm_dp_dpcd_readb(&anx78xx->aux, DP_MAX_LINK_RATE, &dp_bw); if (err < 0) return err; switch (dp_bw) { case DP_LINK_BW_1_62: case DP_LINK_BW_2_7: case DP_LINK_BW_5_4: break; default: DRM_DEBUG_KMS("DP bandwidth (%#02x) not supported\n", dp_bw); return -EINVAL; } err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL1_REG, SP_VIDEO_MUTE); if (err) return err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL1_REG, SP_VIDEO_EN); if (err) return err; /* Get DPCD info */ err = drm_dp_dpcd_read(&anx78xx->aux, DP_DPCD_REV, &anx78xx->dpcd, DP_RECEIVER_CAP_SIZE); if (err < 0) { DRM_ERROR("Failed to read DPCD: %d\n", err); return err; } /* Clear channel x SERDES power down */ err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_ANALOG_POWER_DOWN_REG, SP_CH0_PD); if (err) return err; /* * Power up the sink (DP_SET_POWER register is only available on DPCD * v1.1 and later). */ if (anx78xx->dpcd[DP_DPCD_REV] >= 0x11) { err = drm_dp_dpcd_readb(&anx78xx->aux, DP_SET_POWER, &dpcd[0]); if (err < 0) { DRM_ERROR("Failed to read DP_SET_POWER register: %d\n", err); return err; } dpcd[0] &= ~DP_SET_POWER_MASK; dpcd[0] |= DP_SET_POWER_D0; err = drm_dp_dpcd_writeb(&anx78xx->aux, DP_SET_POWER, dpcd[0]); if (err < 0) { DRM_ERROR("Failed to power up DisplayPort link: %d\n", err); return err; } /* * 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(1000, 2000); } /* Possibly enable downspread on the sink */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_DOWNSPREAD_CTRL1_REG, 0); if (err) return err; if (anx78xx->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5) { DRM_DEBUG("Enable downspread on the sink\n"); /* 4000PPM */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_DOWNSPREAD_CTRL1_REG, 8); if (err) return err; err = drm_dp_dpcd_writeb(&anx78xx->aux, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); if (err < 0) return err; } else { err = drm_dp_dpcd_writeb(&anx78xx->aux, DP_DOWNSPREAD_CTRL, 0); if (err < 0) return err; } /* Set the lane count and the link rate on the sink */ if (drm_dp_enhanced_frame_cap(anx78xx->dpcd)) err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_SYSTEM_CTRL_BASE + 4, SP_ENHANCED_MODE); else err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P0], SP_DP_SYSTEM_CTRL_BASE + 4, SP_ENHANCED_MODE); if (err) return err; err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_MAIN_LINK_BW_SET_REG, anx78xx->dpcd[DP_MAX_LINK_RATE]); if (err) return err; dpcd[1] = drm_dp_max_lane_count(anx78xx->dpcd); if (drm_dp_enhanced_frame_cap(anx78xx->dpcd)) dpcd[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; err = drm_dp_dpcd_write(&anx78xx->aux, DP_LINK_BW_SET, dpcd, sizeof(dpcd)); if (err < 0) { DRM_ERROR("Failed to configure link: %d\n", err); return err; } /* Start training on the source */ err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_LT_CTRL_REG, SP_LT_EN); if (err) return err; return 0; } static int anx78xx_config_dp_output(struct anx78xx *anx78xx) { int err; err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL1_REG, SP_VIDEO_MUTE); if (err) return err; /* Enable DP output */ err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_VID_CTRL1_REG, SP_VIDEO_EN); if (err) return err; return 0; } static int anx78xx_send_video_infoframe(struct anx78xx *anx78xx, struct hdmi_avi_infoframe *frame) { u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; int err; err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer)); if (err < 0) { DRM_ERROR("Failed to pack AVI infoframe: %d\n", err); return err; } err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P0], SP_PACKET_SEND_CTRL_REG, SP_AVI_IF_EN); if (err) return err; err = regmap_bulk_write(anx78xx->map[I2C_IDX_TX_P2], SP_INFOFRAME_AVI_DB1_REG, buffer, frame->length); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_PACKET_SEND_CTRL_REG, SP_AVI_IF_UD); if (err) return err; err = anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P0], SP_PACKET_SEND_CTRL_REG, SP_AVI_IF_EN); if (err) return err; return 0; } static int anx78xx_get_downstream_info(struct anx78xx *anx78xx) { u8 value; int err; err = drm_dp_dpcd_readb(&anx78xx->aux, DP_SINK_COUNT, &value); if (err < 0) { DRM_ERROR("Get sink count failed %d\n", err); return err; } if (!DP_GET_SINK_COUNT(value)) { DRM_ERROR("Downstream disconnected\n"); return -EIO; } return 0; } static int anx78xx_get_modes(struct drm_connector *connector) { struct anx78xx *anx78xx = connector_to_anx78xx(connector); int err, num_modes = 0; if (WARN_ON(!anx78xx->powered)) return 0; if (anx78xx->edid) return drm_add_edid_modes(connector, anx78xx->edid); mutex_lock(&anx78xx->lock); err = anx78xx_get_downstream_info(anx78xx); if (err) { DRM_ERROR("Failed to get downstream info: %d\n", err); goto unlock; } anx78xx->edid = drm_get_edid(connector, &anx78xx->aux.ddc); if (!anx78xx->edid) { DRM_ERROR("Failed to read EDID\n"); goto unlock; } err = drm_connector_update_edid_property(connector, anx78xx->edid); if (err) { DRM_ERROR("Failed to update EDID property: %d\n", err); goto unlock; } num_modes = drm_add_edid_modes(connector, anx78xx->edid); unlock: mutex_unlock(&anx78xx->lock); return num_modes; } static const struct drm_connector_helper_funcs anx78xx_connector_helper_funcs = { .get_modes = anx78xx_get_modes, }; static enum drm_connector_status anx78xx_detect(struct drm_connector *connector, bool force) { struct anx78xx *anx78xx = connector_to_anx78xx(connector); if (!gpiod_get_value(anx78xx->pdata.gpiod_hpd)) return connector_status_disconnected; return connector_status_connected; } static const struct drm_connector_funcs anx78xx_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .detect = anx78xx_detect, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static int anx78xx_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct anx78xx *anx78xx = bridge_to_anx78xx(bridge); int err; if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; } if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; } /* Register aux channel */ anx78xx->aux.name = "DP-AUX"; anx78xx->aux.dev = &anx78xx->client->dev; anx78xx->aux.drm_dev = bridge->dev; anx78xx->aux.transfer = anx78xx_aux_transfer; err = drm_dp_aux_register(&anx78xx->aux); if (err < 0) { DRM_ERROR("Failed to register aux channel: %d\n", err); return err; } err = drm_connector_init(bridge->dev, &anx78xx->connector, &anx78xx_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); if (err) { DRM_ERROR("Failed to initialize connector: %d\n", err); goto aux_unregister; } drm_connector_helper_add(&anx78xx->connector, &anx78xx_connector_helper_funcs); anx78xx->connector.polled = DRM_CONNECTOR_POLL_HPD; err = drm_connector_attach_encoder(&anx78xx->connector, bridge->encoder); if (err) { DRM_ERROR("Failed to link up connector to encoder: %d\n", err); goto connector_cleanup; } err = drm_connector_register(&anx78xx->connector); if (err) { DRM_ERROR("Failed to register connector: %d\n", err); goto connector_cleanup; } return 0; connector_cleanup: drm_connector_cleanup(&anx78xx->connector); aux_unregister: drm_dp_aux_unregister(&anx78xx->aux); return err; } static void anx78xx_bridge_detach(struct drm_bridge *bridge) { drm_dp_aux_unregister(&bridge_to_anx78xx(bridge)->aux); } static enum drm_mode_status anx78xx_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) { if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; /* Max 1200p at 5.4 Ghz, one lane */ if (mode->clock > 154000) return MODE_CLOCK_HIGH; return MODE_OK; } static void anx78xx_bridge_disable(struct drm_bridge *bridge) { struct anx78xx *anx78xx = bridge_to_anx78xx(bridge); /* Power off all modules except configuration registers access */ anx78xx_set_bits(anx78xx->map[I2C_IDX_TX_P2], SP_POWERDOWN_CTRL_REG, SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD); } static void anx78xx_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { struct anx78xx *anx78xx = bridge_to_anx78xx(bridge); struct hdmi_avi_infoframe frame; int err; if (WARN_ON(!anx78xx->powered)) return; mutex_lock(&anx78xx->lock); err = drm_hdmi_avi_infoframe_from_display_mode(&frame, &anx78xx->connector, adjusted_mode); if (err) { DRM_ERROR("Failed to setup AVI infoframe: %d\n", err); goto unlock; } err = anx78xx_send_video_infoframe(anx78xx, &frame); if (err) DRM_ERROR("Failed to send AVI infoframe: %d\n", err); unlock: mutex_unlock(&anx78xx->lock); } static void anx78xx_bridge_enable(struct drm_bridge *bridge) { struct anx78xx *anx78xx = bridge_to_anx78xx(bridge); int err; err = anx78xx_start(anx78xx); if (err) { DRM_ERROR("Failed to initialize: %d\n", err); return; } err = anx78xx_set_hpd(anx78xx); if (err) DRM_ERROR("Failed to set HPD: %d\n", err); } static const struct drm_bridge_funcs anx78xx_bridge_funcs = { .attach = anx78xx_bridge_attach, .detach = anx78xx_bridge_detach, .mode_valid = anx78xx_bridge_mode_valid, .disable = anx78xx_bridge_disable, .mode_set = anx78xx_bridge_mode_set, .enable = anx78xx_bridge_enable, }; static irqreturn_t anx78xx_hpd_threaded_handler(int irq, void *data) { struct anx78xx *anx78xx = data; int err; if (anx78xx->powered) return IRQ_HANDLED; mutex_lock(&anx78xx->lock); /* Cable is pulled, power on the chip */ anx78xx_poweron(anx78xx); err = anx78xx_enable_interrupts(anx78xx); if (err) DRM_ERROR("Failed to enable interrupts: %d\n", err); mutex_unlock(&anx78xx->lock); return IRQ_HANDLED; } static int anx78xx_handle_dp_int_1(struct anx78xx *anx78xx, u8 irq) { int err; DRM_DEBUG_KMS("Handle DP interrupt 1: %02x\n", irq); err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_DP_INT_STATUS1_REG, irq); if (err) return err; if (irq & SP_TRAINING_FINISH) { DRM_DEBUG_KMS("IRQ: hardware link training finished\n"); err = anx78xx_config_dp_output(anx78xx); } return err; } static bool anx78xx_handle_common_int_4(struct anx78xx *anx78xx, u8 irq) { bool event = false; int err; DRM_DEBUG_KMS("Handle common interrupt 4: %02x\n", irq); err = regmap_write(anx78xx->map[I2C_IDX_TX_P2], SP_COMMON_INT_STATUS4_REG, irq); if (err) { DRM_ERROR("Failed to write SP_COMMON_INT_STATUS4 %d\n", err); return event; } if (irq & SP_HPD_LOST) { DRM_DEBUG_KMS("IRQ: Hot plug detect - cable is pulled out\n"); event = true; anx78xx_poweroff(anx78xx); /* Free cached EDID */ kfree(anx78xx->edid); anx78xx->edid = NULL; } else if (irq & SP_HPD_PLUG) { DRM_DEBUG_KMS("IRQ: Hot plug detect - cable plug\n"); event = true; } return event; } static void anx78xx_handle_hdmi_int_1(struct anx78xx *anx78xx, u8 irq) { unsigned int value; int err; DRM_DEBUG_KMS("Handle HDMI interrupt 1: %02x\n", irq); err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_INT_STATUS1_REG, irq); if (err) { DRM_ERROR("Write HDMI int 1 failed: %d\n", err); return; } if ((irq & SP_CKDT_CHG) || (irq & SP_SCDT_CHG)) { DRM_DEBUG_KMS("IRQ: HDMI input detected\n"); err = regmap_read(anx78xx->map[I2C_IDX_RX_P0], SP_SYSTEM_STATUS_REG, &value); if (err) { DRM_ERROR("Read system status reg failed: %d\n", err); return; } if (!(value & SP_TMDS_CLOCK_DET)) { DRM_DEBUG_KMS("IRQ: *** Waiting for HDMI clock ***\n"); return; } if (!(value & SP_TMDS_DE_DET)) { DRM_DEBUG_KMS("IRQ: *** Waiting for HDMI signal ***\n"); return; } err = anx78xx_dp_link_training(anx78xx); if (err) DRM_ERROR("Failed to start link training: %d\n", err); } } static irqreturn_t anx78xx_intp_threaded_handler(int unused, void *data) { struct anx78xx *anx78xx = data; bool event = false; unsigned int irq; int err; mutex_lock(&anx78xx->lock); err = regmap_read(anx78xx->map[I2C_IDX_TX_P2], SP_DP_INT_STATUS1_REG, &irq); if (err) { DRM_ERROR("Failed to read DP interrupt 1 status: %d\n", err); goto unlock; } if (irq) anx78xx_handle_dp_int_1(anx78xx, irq); err = regmap_read(anx78xx->map[I2C_IDX_TX_P2], SP_COMMON_INT_STATUS4_REG, &irq); if (err) { DRM_ERROR("Failed to read common interrupt 4 status: %d\n", err); goto unlock; } if (irq) event = anx78xx_handle_common_int_4(anx78xx, irq); /* Make sure we are still powered after handle HPD events */ if (!anx78xx->powered) goto unlock; err = regmap_read(anx78xx->map[I2C_IDX_RX_P0], SP_INT_STATUS1_REG, &irq); if (err) { DRM_ERROR("Failed to read HDMI int 1 status: %d\n", err); goto unlock; } if (irq) anx78xx_handle_hdmi_int_1(anx78xx, irq); unlock: mutex_unlock(&anx78xx->lock); if (event) drm_helper_hpd_irq_event(anx78xx->connector.dev); return IRQ_HANDLED; } static void unregister_i2c_dummy_clients(struct anx78xx *anx78xx) { unsigned int i; for (i = 0; i < ARRAY_SIZE(anx78xx->i2c_dummy); i++) i2c_unregister_device(anx78xx->i2c_dummy[i]); } static const struct regmap_config anx78xx_regmap_config = { .reg_bits = 8, .val_bits = 8, }; static const u16 anx78xx_chipid_list[] = { 0x7808, 0x7812, 0x7814, 0x7816, 0x7818, }; static int anx78xx_i2c_probe(struct i2c_client *client) { struct anx78xx *anx78xx; struct anx78xx_platform_data *pdata; unsigned int i, idl, idh, version; const u8 *i2c_addresses; bool found = false; int err; anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL); if (!anx78xx) return -ENOMEM; pdata = &anx78xx->pdata; mutex_init(&anx78xx->lock); anx78xx->bridge.of_node = client->dev.of_node; anx78xx->client = client; i2c_set_clientdata(client, anx78xx); err = anx78xx_init_pdata(anx78xx); if (err) { if (err != -EPROBE_DEFER) DRM_ERROR("Failed to initialize pdata: %d\n", err); return err; } pdata->hpd_irq = gpiod_to_irq(pdata->gpiod_hpd); if (pdata->hpd_irq < 0) { DRM_ERROR("Failed to get HPD IRQ: %d\n", pdata->hpd_irq); return -ENODEV; } pdata->intp_irq = client->irq; if (!pdata->intp_irq) { DRM_ERROR("Failed to get CABLE_DET and INTP IRQ\n"); return -ENODEV; } /* Map slave addresses of ANX7814 */ i2c_addresses = device_get_match_data(&client->dev); for (i = 0; i < I2C_NUM_ADDRESSES; i++) { struct i2c_client *i2c_dummy; i2c_dummy = i2c_new_dummy_device(client->adapter, i2c_addresses[i] >> 1); if (IS_ERR(i2c_dummy)) { err = PTR_ERR(i2c_dummy); DRM_ERROR("Failed to reserve I2C bus %02x: %d\n", i2c_addresses[i], err); goto err_unregister_i2c; } anx78xx->i2c_dummy[i] = i2c_dummy; anx78xx->map[i] = devm_regmap_init_i2c(anx78xx->i2c_dummy[i], &anx78xx_regmap_config); if (IS_ERR(anx78xx->map[i])) { err = PTR_ERR(anx78xx->map[i]); DRM_ERROR("Failed regmap initialization %02x\n", i2c_addresses[i]); goto err_unregister_i2c; } } /* Look for supported chip ID */ anx78xx_poweron(anx78xx); err = regmap_read(anx78xx->map[I2C_IDX_TX_P2], SP_DEVICE_IDL_REG, &idl); if (err) goto err_poweroff; err = regmap_read(anx78xx->map[I2C_IDX_TX_P2], SP_DEVICE_IDH_REG, &idh); if (err) goto err_poweroff; anx78xx->chipid = (u8)idl | ((u8)idh << 8); err = regmap_read(anx78xx->map[I2C_IDX_TX_P2], SP_DEVICE_VERSION_REG, &version); if (err) goto err_poweroff; for (i = 0; i < ARRAY_SIZE(anx78xx_chipid_list); i++) { if (anx78xx->chipid == anx78xx_chipid_list[i]) { DRM_INFO("Found ANX%x (ver. %d) SlimPort Transmitter\n", anx78xx->chipid, version); found = true; break; } } if (!found) { DRM_ERROR("ANX%x (ver. %d) not supported by this driver\n", anx78xx->chipid, version); err = -ENODEV; goto err_poweroff; } err = devm_request_threaded_irq(&client->dev, pdata->hpd_irq, NULL, anx78xx_hpd_threaded_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "anx78xx-hpd", anx78xx); if (err) { DRM_ERROR("Failed to request CABLE_DET threaded IRQ: %d\n", err); goto err_poweroff; } err = devm_request_threaded_irq(&client->dev, pdata->intp_irq, NULL, anx78xx_intp_threaded_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "anx78xx-intp", anx78xx); if (err) { DRM_ERROR("Failed to request INTP threaded IRQ: %d\n", err); goto err_poweroff; } anx78xx->bridge.funcs = &anx78xx_bridge_funcs; drm_bridge_add(&anx78xx->bridge); /* If cable is pulled out, just poweroff and wait for HPD event */ if (!gpiod_get_value(anx78xx->pdata.gpiod_hpd)) anx78xx_poweroff(anx78xx); return 0; err_poweroff: anx78xx_poweroff(anx78xx); err_unregister_i2c: unregister_i2c_dummy_clients(anx78xx); return err; } static void anx78xx_i2c_remove(struct i2c_client *client) { struct anx78xx *anx78xx = i2c_get_clientdata(client); drm_bridge_remove(&anx78xx->bridge); unregister_i2c_dummy_clients(anx78xx); kfree(anx78xx->edid); } static const struct of_device_id anx78xx_match_table[] = { { .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses }, { .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses }, { .compatible = "analogix,anx7814", .data = anx781x_i2c_addresses }, { .compatible = "analogix,anx7816", .data = anx781x_i2c_addresses }, { .compatible = "analogix,anx7818", .data = anx781x_i2c_addresses }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, anx78xx_match_table); static struct i2c_driver anx78xx_driver = { .driver = { .name = "anx7814", .of_match_table = anx78xx_match_table, }, .probe = anx78xx_i2c_probe, .remove = anx78xx_i2c_remove, }; module_i2c_driver(anx78xx_driver); MODULE_DESCRIPTION("ANX78xx SlimPort Transmitter driver"); MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1