diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/analogix-anx78xx.c')
-rw-r--r-- | drivers/gpu/drm/bridge/analogix-anx78xx.c | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index 3c7cc5af735c..274989f96a91 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -19,6 +19,7 @@ #include <linux/types.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> @@ -38,12 +39,20 @@ #define AUX_CH_BUFFER_SIZE 16 #define AUX_WAIT_TIMEOUT_MS 15 -static const u8 anx78xx_i2c_addresses[] = { - [I2C_IDX_TX_P0] = TX_P0, - [I2C_IDX_TX_P1] = TX_P1, - [I2C_IDX_TX_P2] = TX_P2, - [I2C_IDX_RX_P0] = RX_P0, - [I2C_IDX_RX_P1] = RX_P1, +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 { @@ -62,7 +71,6 @@ struct anx78xx { struct i2c_client *client; struct edid *edid; struct drm_connector connector; - struct drm_dp_link link; struct anx78xx_platform_data pdata; struct mutex lock; @@ -715,7 +723,9 @@ static int anx78xx_init_pdata(struct anx78xx *anx78xx) /* 1.0V digital core power regulator */ pdata->dvdd10 = devm_regulator_get(dev, "dvdd10"); if (IS_ERR(pdata->dvdd10)) { - DRM_ERROR("DVDD10 regulator not found\n"); + if (PTR_ERR(pdata->dvdd10) != -EPROBE_DEFER) + DRM_ERROR("DVDD10 regulator not found\n"); + return PTR_ERR(pdata->dvdd10); } @@ -737,7 +747,7 @@ static int anx78xx_init_pdata(struct anx78xx *anx78xx) static int anx78xx_dp_link_training(struct anx78xx *anx78xx) { - u8 dp_bw, value; + u8 dp_bw, dpcd[2]; int err; err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_MUTE_CTRL_REG, @@ -790,18 +800,34 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx) if (err) return err; - /* Check link capabilities */ - err = drm_dp_link_probe(&anx78xx->aux, &anx78xx->link); - if (err < 0) { - DRM_ERROR("Failed to probe link capabilities: %d\n", 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; + } - /* Power up the sink */ - err = drm_dp_link_power_up(&anx78xx->aux, &anx78xx->link); - if (err < 0) { - DRM_ERROR("Failed to power up DisplayPort link: %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 */ @@ -840,15 +866,22 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx) if (err) return err; - value = drm_dp_link_rate_to_bw_code(anx78xx->link.rate); + dpcd[0] = drm_dp_max_link_rate(anx78xx->dpcd); + dpcd[0] = drm_dp_link_rate_to_bw_code(dpcd[0]); err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_MAIN_LINK_BW_SET_REG, value); + SP_DP_MAIN_LINK_BW_SET_REG, dpcd[0]); if (err) return err; - err = drm_dp_link_configure(&anx78xx->aux, &anx78xx->link); + 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 DisplayPort link: %d\n", err); + DRM_ERROR("Failed to configure link: %d\n", err); return err; } @@ -1301,6 +1334,7 @@ static const struct regmap_config anx78xx_regmap_config = { }; static const u16 anx78xx_chipid_list[] = { + 0x7808, 0x7812, 0x7814, 0x7818, @@ -1312,6 +1346,7 @@ 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; @@ -1332,7 +1367,9 @@ static int anx78xx_i2c_probe(struct i2c_client *client, err = anx78xx_init_pdata(anx78xx); if (err) { - DRM_ERROR("Failed to initialize pdata: %d\n", err); + if (err != -EPROBE_DEFER) + DRM_ERROR("Failed to initialize pdata: %d\n", err); + return err; } @@ -1349,22 +1386,26 @@ static int anx78xx_i2c_probe(struct i2c_client *client, } /* Map slave addresses of ANX7814 */ + i2c_addresses = device_get_match_data(&client->dev); for (i = 0; i < I2C_NUM_ADDRESSES; i++) { - anx78xx->i2c_dummy[i] = i2c_new_dummy(client->adapter, - anx78xx_i2c_addresses[i] >> 1); - if (!anx78xx->i2c_dummy[i]) { - err = -ENOMEM; - DRM_ERROR("Failed to reserve I2C bus %02x\n", - anx78xx_i2c_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", - anx78xx_i2c_addresses[i]); + i2c_addresses[i]); goto err_unregister_i2c; } } @@ -1463,7 +1504,10 @@ MODULE_DEVICE_TABLE(i2c, anx78xx_id); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id anx78xx_match_table[] = { - { .compatible = "analogix,anx7814", }, + { .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses }, + { .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses }, + { .compatible = "analogix,anx7814", .data = anx781x_i2c_addresses }, + { .compatible = "analogix,anx7818", .data = anx781x_i2c_addresses }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, anx78xx_match_table); |