/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include "sn65dsi86bridge.h" #define BRIDGE_GETHIGHERBYTE(x) ((uint8_t)((x & 0xff00) >> 8)) #define BRIDGE_GETLOWERBYTE(x) ((uint8_t)(x & 0x00ff)) /* fudge factor required to account for 8b/10b encoding */ #define DP_CLK_FUDGE_NUM 10 #define DP_CLK_FUDGE_DEN 8 /* DPCD */ #define DP_BRIDGE_DPCD_REV 0x700 #define DP_BRIDGE_11 0x00 #define DP_BRIDGE_12 0x01 #define DP_BRIDGE_13 0x02 #define DP_BRIDGE_14 0x03 #define DP_BRIDGE_CONFIGURATION_SET 0x10a #define DP_MAX_LINK_RATE 0x001 #define DP_MAX_LANE_COUNT 0x002 #define DP_SUPPORTED_LINK_RATES 0x010 /* eDP 1.4 */ #define DP_MAX_LINK_RATE 0x001 #define DP_MAX_SUPPORTED_RATES 8 /* 16-bit little-endian */ #define DP_LANE_COUNT_MASK 0xf /* link configuration */ #define DP_LINK_BW_SET 0x100 #define DP_LINK_BW_1_62 0x06 #define DP_LINK_BW_2_7 0x0a #define DP_LINK_BW_5_4 0x14 #define AUX_CMD_SEND 0x1 #define MIN_DSI_CLK_FREQ_MHZ 40 #define MAX_DSI_CLK_FREQ_MHZ 750 enum bridge_regs { SN_DPPLL_SRC_REG = 0x0A, SN_PLL_ENABLE_REG = 0x0D, SN_DSI_LANES_REG = 0x10, SN_DSIA_CLK_FREQ_REG = 0x12, SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG = 0x20, SN_CHA_ACTIVE_LINE_LENGTH_HIGH_REG = 0x21, SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG = 0x24, SN_CHA_VERTICAL_DISPLAY_SIZE_HIGH_REG = 0x25, SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG = 0x2C, SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG = 0x2D, SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG = 0x30, SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG = 0x31, SN_CHA_HORIZONTAL_BACK_PORCH_REG = 0x34, SN_CHA_VERTICAL_BACK_PORCH_REG = 0x36, SN_CHA_HORIZONTAL_FRONT_PORCH_REG = 0x38, SN_CHA_VERTICAL_FRONT_PORCH_REG = 0x3A, SN_COLOR_BAR_REG = 0x3C, SN_ENH_FRAME_REG = 0x5A, SN_DATA_FORMAT_REG = 0x5B, SN_HPD_DISABLE_REG = 0x5C, SN_I2C_CLAIM_ADDR_EN1 = 0x60, SN_AUX_WDATA_REG_0 = 0x64, SN_AUX_WDATA_REG_1 = 0x65, SN_AUX_WDATA_REG_2 = 0x66, SN_AUX_WDATA_REG_3 = 0x67, SN_AUX_WDATA_REG_4 = 0x68, SN_AUX_WDATA_REG_5 = 0x69, SN_AUX_WDATA_REG_6 = 0x6A, SN_AUX_WDATA_REG_7 = 0x6B, SN_AUX_WDATA_REG_8 = 0x6C, SN_AUX_WDATA_REG_9 = 0x6D, SN_AUX_WDATA_REG_10 = 0x6E, SN_AUX_WDATA_REG_11 = 0x6F, SN_AUX_WDATA_REG_12 = 0x70, SN_AUX_WDATA_REG_13 = 0x71, SN_AUX_WDATA_REG_14 = 0x72, SN_AUX_WDATA_REG_15 = 0x73, SN_AUX_ADDR_19_16_REG = 0x74, SN_AUX_ADDR_15_8_REG = 0x75, SN_AUX_ADDR_7_0_REG = 0x76, SN_AUX_LENGTH_REG = 0x77, SN_AUX_CMD_REG = 0x78, SN_AUX_RDATA_REG_0 = 0x79, SN_AUX_RDATA_REG_1 = 0x7A, SN_AUX_RDATA_REG_2 = 0x7B, SN_AUX_RDATA_REG_3 = 0x7C, SN_AUX_RDATA_REG_4 = 0x7D, SN_AUX_RDATA_REG_5 = 0x7E, SN_AUX_RDATA_REG_6 = 0x7F, SN_AUX_RDATA_REG_7 = 0x80, SN_AUX_RDATA_REG_8 = 0x81, SN_AUX_RDATA_REG_9 = 0x82, SN_AUX_RDATA_REG_10 = 0x83, SN_AUX_RDATA_REG_11 = 0x84, SN_AUX_RDATA_REG_12 = 0x85, SN_AUX_RDATA_REG_13 = 0x86, SN_AUX_RDATA_REG_14 = 0x87, SN_AUX_RDATA_REG_15 = 0x88, SN_SSC_CONFIG_REG = 0x93, SN_DATARATE_CONFIG_REG = 0x94, SN_ML_TX_MODE_REG = 0x96, SN_AUX_CMD_STATUS_REG = 0xF4, }; enum { HPD_ENABLE = 0x0, HPD_DISABLE = 0x1, }; enum { SOT_ERR_TOL_DSI = 0x0, CHB_DSI_LANES = 0x1, CHA_DSI_LANES = 0x2, DSI_CHANNEL_MODE = 0x3, LEFT_RIGHT_PIXELS = 0x4, }; enum vstream_config { VSTREAM_DISABLE = 0, VSTREAM_ENABLE = 1, }; enum aux_cmd_status { NAT_I2C_FAIL = 1 << 6, AUX_SHORT = 1 << 5, AUX_DFER = 1 << 4, AUX_RPLY_TOUT = 1 << 3, SEND_INT = 1 << 0, }; enum ml_tx_mode { MAIN_LINK_OFF = 0x0, NORMAL_MODE = 0x1, TPS1 = 0x2, TPS2 = 0x3, TPS3 = 0x4, PRBS7 = 0x5, HBR2_COMPLIANCE_EYE_PATTERN = 0x6, SYMBOL_ERR_RATE_MEASUREMENT_PATTERN = 0x7, CUTSOM_PATTERN = 0x8, FAST_LINK_TRAINING = 0x9, SEMI_AUTO_LINK_TRAINING = 0xa, REDRIVER_SEMI_AUTO_LINK_TRAINING = 0xb, }; /* * LUT index corresponds to register value and LUT values corresponds * to dp data rate supported by the bridge in Mbps unit. */ static const unsigned int sn65dsi86_bridge_dp_rate_lut[] = { 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 }; static cb_err_t sn65dsi86_bridge_aux_request(uint8_t bus, uint8_t chip, unsigned int target_reg, unsigned int total_size, enum aux_request request, uint8_t *data) { int i; uint32_t length; uint8_t buf; uint8_t reg; /* Clear old status flags just in case they're left over from a previous transfer. */ i2c_writeb(bus, chip, SN_AUX_CMD_STATUS_REG, NAT_I2C_FAIL | AUX_SHORT | AUX_DFER | AUX_RPLY_TOUT | SEND_INT); while (total_size) { length = MIN(total_size, DP_AUX_MAX_PAYLOAD_BYTES); total_size -= length; enum i2c_over_aux cmd = dp_get_aux_cmd(request, total_size); if (i2c_writeb(bus, chip, SN_AUX_CMD_REG, (cmd << 4)) || i2c_writeb(bus, chip, SN_AUX_ADDR_19_16_REG, (target_reg >> 16) & 0xF) || i2c_writeb(bus, chip, SN_AUX_ADDR_15_8_REG, (target_reg >> 8) & 0xFF) || i2c_writeb(bus, chip, SN_AUX_ADDR_7_0_REG, (target_reg) & 0xFF) || i2c_writeb(bus, chip, SN_AUX_LENGTH_REG, length)) return CB_ERR; if (dp_aux_request_is_write(request)) { reg = SN_AUX_WDATA_REG_0; for (i = 0; i < length; i++) if (i2c_writeb(bus, chip, reg++, *data++)) return CB_ERR; } if (i2c_writeb(bus, chip, SN_AUX_CMD_REG, AUX_CMD_SEND | (cmd << 4))) return CB_ERR; if (!wait_ms(100, !i2c_readb(bus, chip, SN_AUX_CMD_REG, &buf) && !(buf & AUX_CMD_SEND))) { printk(BIOS_ERR, "ERROR: AUX_CMD_SEND not acknowledged\n"); return CB_ERR; } if (i2c_readb(bus, chip, SN_AUX_CMD_STATUS_REG, &buf)) return CB_ERR; if (buf & (NAT_I2C_FAIL | AUX_SHORT | AUX_DFER | AUX_RPLY_TOUT)) { printk(BIOS_ERR, "ERROR: AUX command failed, status = %#x\n", buf); return CB_ERR; } if (!dp_aux_request_is_write(request)) { reg = SN_AUX_RDATA_REG_0; for (i = 0; i < length; i++) { if (i2c_readb(bus, chip, reg++, &buf)) return CB_ERR; *data++ = buf; } } } return CB_SUCCESS; } cb_err_t sn65dsi86_bridge_read_edid(uint8_t bus, uint8_t chip, struct edid *out) { cb_err_t err; u8 edid[EDID_LENGTH * 2]; int edid_size = EDID_LENGTH; uint8_t reg_addr = 0; err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, 1, I2C_RAW_WRITE, ®_addr); if (!err) err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, EDID_LENGTH, I2C_RAW_READ_AND_STOP, edid); if (err) { printk(BIOS_ERR, "ERROR: Failed to read EDID.\n"); return err; } if (edid[EDID_EXTENSION_FLAG]) { edid_size += EDID_LENGTH; reg_addr = EDID_LENGTH; err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, 1, I2C_RAW_WRITE, ®_addr); if (!err) err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, EDID_LENGTH, I2C_RAW_READ_AND_STOP, &edid[EDID_LENGTH]); if (err) { printk(BIOS_ERR, "Failed to read EDID ext block.\n"); return err; } } if (decode_edid(edid, edid_size, out) != EDID_CONFORMANT) { printk(BIOS_ERR, "ERROR: Failed to decode EDID.\n"); return CB_ERR; } return CB_SUCCESS; } static void sn65dsi86_bridge_valid_dp_rates(uint8_t bus, uint8_t chip, bool rate_valid[]) { unsigned int rate_per_200khz; uint8_t dpcd_val; int i, j; sn65dsi86_bridge_aux_request(bus, chip, DP_BRIDGE_DPCD_REV, 1, DPCD_READ, &dpcd_val); if (dpcd_val >= DP_BRIDGE_14) { /* eDP 1.4 devices must provide a custom table */ uint16_t sink_rates[DP_MAX_SUPPORTED_RATES] = {0}; sn65dsi86_bridge_aux_request(bus, chip, DP_SUPPORTED_LINK_RATES, sizeof(sink_rates), DPCD_READ, (void *)sink_rates); for (i = 0; i < ARRAY_SIZE(sink_rates); i++) { rate_per_200khz = le16_to_cpu(sink_rates[i]); if (!rate_per_200khz) break; for (j = 0; j < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut); j++) { if (sn65dsi86_bridge_dp_rate_lut[j] * (MHz / KHz) == rate_per_200khz * 200) rate_valid[j] = true; } } for (i = 0; i < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut); i++) { if (rate_valid[i]) return; } printk(BIOS_ERR, "No matching eDP rates in table; falling back\n"); } /* On older versions best we can do is use DP_MAX_LINK_RATE */ sn65dsi86_bridge_aux_request(bus, chip, DP_MAX_LINK_RATE, 1, DPCD_READ, &dpcd_val); switch (dpcd_val) { default: printk(BIOS_ERR, "Unexpected max rate (%#x); assuming 5.4 GHz\n", (int)dpcd_val); /* fall through */ case DP_LINK_BW_5_4: rate_valid[7] = 1; /* fall through */ case DP_LINK_BW_2_7: rate_valid[4] = 1; /* fall through */ case DP_LINK_BW_1_62: rate_valid[1] = 1; break; } } static void sn65dsi86_bridge_set_dsi_clock_range(uint8_t bus, uint8_t chip, struct edid *edid, uint32_t num_of_lanes, uint32_t bpp) { uint64_t pixel_clk_hz; uint64_t stream_bit_rate_mhz; uint64_t min_req_dsi_clk; pixel_clk_hz = edid->mode.pixel_clock * KHz; stream_bit_rate_mhz = (pixel_clk_hz * bpp) / MHz; /* For TI the clock frequencies are half the bit rates */ min_req_dsi_clk = stream_bit_rate_mhz / (num_of_lanes * 2); /* for each increment in val, frequency increases by 5MHz */ min_req_dsi_clk = MAX(MIN_DSI_CLK_FREQ_MHZ, MIN(MAX_DSI_CLK_FREQ_MHZ, min_req_dsi_clk)) / 5; i2c_writeb(bus, chip, SN_DSIA_CLK_FREQ_REG, min_req_dsi_clk); } static void sn65dsi86_bridge_set_dp_clock_range(uint8_t bus, uint8_t chip, struct edid *edid, uint32_t num_of_lanes) { uint64_t stream_bit_rate_khz; bool rate_valid[ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut)] = { }; uint64_t dp_rate_mhz; int dp_rate_idx, i; stream_bit_rate_khz = edid->mode.pixel_clock * 18; /* Calculate minimum DP data rate, taking 80% as per DP spec */ dp_rate_mhz = DIV_ROUND_UP(stream_bit_rate_khz * DP_CLK_FUDGE_NUM, KHz * num_of_lanes * DP_CLK_FUDGE_DEN); for (i = 0; i < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut) - 1; i++) if (sn65dsi86_bridge_dp_rate_lut[i] > dp_rate_mhz) break; sn65dsi86_bridge_valid_dp_rates(bus, chip, rate_valid); /* Train until we run out of rates */ for (dp_rate_idx = i; dp_rate_idx < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut); dp_rate_idx++) if (rate_valid[dp_rate_idx]) break; if (dp_rate_idx < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut)) i2c_write_field(bus, chip, SN_DATARATE_CONFIG_REG, dp_rate_idx, 8, 5); else printk(BIOS_ERR, "ERROR: valid dp rate not found"); } static void sn65dsi86_bridge_set_bridge_active_timing(uint8_t bus, uint8_t chip, struct edid *edid) { i2c_writeb(bus, chip, SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG, BRIDGE_GETLOWERBYTE(edid->mode.ha)); i2c_writeb(bus, chip, SN_CHA_ACTIVE_LINE_LENGTH_HIGH_REG, BRIDGE_GETHIGHERBYTE(edid->mode.ha)); i2c_writeb(bus, chip, SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG, BRIDGE_GETLOWERBYTE(edid->mode.va)); i2c_writeb(bus, chip, SN_CHA_VERTICAL_DISPLAY_SIZE_HIGH_REG, BRIDGE_GETHIGHERBYTE(edid->mode.va)); i2c_writeb(bus, chip, SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG, BRIDGE_GETLOWERBYTE(edid->mode.hspw)); i2c_writeb(bus, chip, SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG, BRIDGE_GETHIGHERBYTE(edid->mode.hspw)); i2c_writeb(bus, chip, SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG, BRIDGE_GETLOWERBYTE(edid->mode.vspw)); i2c_writeb(bus, chip, SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG, BRIDGE_GETHIGHERBYTE(edid->mode.vspw)); i2c_writeb(bus, chip, SN_CHA_HORIZONTAL_BACK_PORCH_REG, edid->mode.hbl - edid->mode.hso - edid->mode.hspw); i2c_writeb(bus, chip, SN_CHA_VERTICAL_BACK_PORCH_REG, edid->mode.vbl - edid->mode.vso - edid->mode.vspw); i2c_writeb(bus, chip, SN_CHA_HORIZONTAL_FRONT_PORCH_REG, edid->mode.hso); i2c_writeb(bus, chip, SN_CHA_VERTICAL_FRONT_PORCH_REG, edid->mode.vso); } static void sn65dsi86_bridge_link_training(uint8_t bus, uint8_t chip) { uint8_t buf; /* enable pll lock */ i2c_writeb(bus, chip, SN_PLL_ENABLE_REG, 0x1); if (!wait_ms(500, !(i2c_readb(bus, chip, SN_DPPLL_SRC_REG, &buf)) && (buf & BIT(7)))) { printk(BIOS_ERR, "ERROR: PLL lock failure\n"); } /* * The SN65DSI86 only supports ASSR Display Authentication method and * this method is enabled by default. An eDP panel must support this * authentication method. We need to enable this method in the eDP panel * at DisplayPort address 0x0010A prior to link training. */ buf = 0x1; sn65dsi86_bridge_aux_request(bus, chip, DP_BRIDGE_CONFIGURATION_SET, 1, DPCD_WRITE, &buf); int i; /* Kernel driver suggests to retry this up to 10 times if it fails. */ for (i = 0; i < 10; i++) { i2c_writeb(bus, chip, SN_ML_TX_MODE_REG, SEMI_AUTO_LINK_TRAINING); if (!wait_ms(500, !(i2c_readb(bus, chip, SN_ML_TX_MODE_REG, &buf)) && (buf == NORMAL_MODE || buf == MAIN_LINK_OFF))) { printk(BIOS_ERR, "ERROR: unexpected link training state: %#x\n", buf); return; } if (buf == NORMAL_MODE) return; } printk(BIOS_ERR, "ERROR: Link training failed 10 times\n"); } void sn65dsi86_backlight_enable(uint8_t bus, uint8_t chip) { uint8_t val = DP_BACKLIGHT_CONTROL_MODE_DPCD; sn65dsi86_bridge_aux_request(bus, chip, DP_BACKLIGHT_MODE_SET, 1, DPCD_WRITE, &val); val = 0xff; sn65dsi86_bridge_aux_request(bus, chip, DP_BACKLIGHT_BRIGHTNESS_MSB, 1, DPCD_WRITE, &val); val = DP_BACKLIGHT_ENABLE; sn65dsi86_bridge_aux_request(bus, chip, DP_DISPLAY_CONTROL_REGISTER, 1, DPCD_WRITE, &val); } static void sn65dsi86_bridge_assr_config(uint8_t bus, uint8_t chip, int enable) { if (enable) i2c_write_field(bus, chip, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 1, 3); else i2c_write_field(bus, chip, SN_ENH_FRAME_REG, VSTREAM_DISABLE, 1, 3); } static int sn65dsi86_bridge_dp_lane_config(uint8_t bus, uint8_t chip) { uint8_t lane_count; sn65dsi86_bridge_aux_request(bus, chip, DP_MAX_LANE_COUNT, 1, DPCD_READ, &lane_count); lane_count &= DP_LANE_COUNT_MASK; i2c_write_field(bus, chip, SN_SSC_CONFIG_REG, MIN(lane_count, 3), 3, 4); return lane_count; } void sn65dsi86_bridge_init(uint8_t bus, uint8_t chip, enum dp_pll_clk_src ref_clk) { /* disable HPD */ i2c_write_field(bus, chip, SN_HPD_DISABLE_REG, HPD_DISABLE, 1, 0); /* set refclk to 19.2 MHZ */ i2c_write_field(bus, chip, SN_DPPLL_SRC_REG, ref_clk, 7, 1); } void sn65dsi86_bridge_configure(uint8_t bus, uint8_t chip, struct edid *edid, uint32_t num_of_lanes, uint32_t dsi_bpp) { int dp_lanes; /* DSI Lanes config */ i2c_write_field(bus, chip, SN_DSI_LANES_REG, (4 - num_of_lanes), 3, 3); /* DP Lane config */ dp_lanes = sn65dsi86_bridge_dp_lane_config(bus, chip); sn65dsi86_bridge_set_dsi_clock_range(bus, chip, edid, num_of_lanes, dsi_bpp); sn65dsi86_bridge_set_dp_clock_range(bus, chip, edid, dp_lanes); /* Disable vstream */ sn65dsi86_bridge_assr_config(bus, chip, 0); sn65dsi86_bridge_link_training(bus, chip); sn65dsi86_bridge_set_bridge_active_timing(bus, chip, edid); /* DP BPP config */ i2c_writeb(bus, chip, SN_DATA_FORMAT_REG, 0x1); /* color bar disabled */ i2c_writeb(bus, chip, SN_COLOR_BAR_REG, 0x5); /* Enable vstream */ sn65dsi86_bridge_assr_config(bus, chip, 1); }