162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 462306a36Sopenharmony_ci * Author: Chris Zhong <zyw@rock-chips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/reset.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "cdn-dp-core.h" 1562306a36Sopenharmony_ci#include "cdn-dp-reg.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define CDN_DP_SPDIF_CLK 200000000 1862306a36Sopenharmony_ci#define FW_ALIVE_TIMEOUT_US 1000000 1962306a36Sopenharmony_ci#define MAILBOX_RETRY_US 1000 2062306a36Sopenharmony_ci#define MAILBOX_TIMEOUT_US 5000000 2162306a36Sopenharmony_ci#define LINK_TRAINING_RETRY_MS 20 2262306a36Sopenharmony_ci#define LINK_TRAINING_TIMEOUT_MS 500 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_civoid cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci writel(clk / 1000000, dp->regs + SW_CLK_H); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid cdn_dp_clock_reset(struct cdn_dp_device *dp) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci u32 val; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci val = DPTX_FRMR_DATA_CLK_RSTN_EN | 3462306a36Sopenharmony_ci DPTX_FRMR_DATA_CLK_EN | 3562306a36Sopenharmony_ci DPTX_PHY_DATA_RSTN_EN | 3662306a36Sopenharmony_ci DPTX_PHY_DATA_CLK_EN | 3762306a36Sopenharmony_ci DPTX_PHY_CHAR_RSTN_EN | 3862306a36Sopenharmony_ci DPTX_PHY_CHAR_CLK_EN | 3962306a36Sopenharmony_ci SOURCE_AUX_SYS_CLK_RSTN_EN | 4062306a36Sopenharmony_ci SOURCE_AUX_SYS_CLK_EN | 4162306a36Sopenharmony_ci DPTX_SYS_CLK_RSTN_EN | 4262306a36Sopenharmony_ci DPTX_SYS_CLK_EN | 4362306a36Sopenharmony_ci CFG_DPTX_VIF_CLK_RSTN_EN | 4462306a36Sopenharmony_ci CFG_DPTX_VIF_CLK_EN; 4562306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_DPTX_CAR); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; 4862306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_PHY_CAR); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci val = SOURCE_PKT_SYS_RSTN_EN | 5162306a36Sopenharmony_ci SOURCE_PKT_SYS_CLK_EN | 5262306a36Sopenharmony_ci SOURCE_PKT_DATA_RSTN_EN | 5362306a36Sopenharmony_ci SOURCE_PKT_DATA_CLK_EN; 5462306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_PKT_CAR); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci val = SPDIF_CDR_CLK_RSTN_EN | 5762306a36Sopenharmony_ci SPDIF_CDR_CLK_EN | 5862306a36Sopenharmony_ci SOURCE_AIF_SYS_RSTN_EN | 5962306a36Sopenharmony_ci SOURCE_AIF_SYS_CLK_EN | 6062306a36Sopenharmony_ci SOURCE_AIF_CLK_RSTN_EN | 6162306a36Sopenharmony_ci SOURCE_AIF_CLK_EN; 6262306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_AIF_CAR); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | 6562306a36Sopenharmony_ci SOURCE_CIPHER_SYS_CLK_EN | 6662306a36Sopenharmony_ci SOURCE_CIPHER_CHAR_CLK_RSTN_EN | 6762306a36Sopenharmony_ci SOURCE_CIPHER_CHAR_CLK_EN; 6862306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_CIPHER_CAR); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | 7162306a36Sopenharmony_ci SOURCE_CRYPTO_SYS_CLK_EN; 7262306a36Sopenharmony_ci writel(val, dp->regs + SOURCE_CRYPTO_CAR); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* enable Mailbox and PIF interrupt */ 7562306a36Sopenharmony_ci writel(0, dp->regs + APB_INT_MASK); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int cdn_dp_mailbox_read(struct cdn_dp_device *dp) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int val, ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR, 8362306a36Sopenharmony_ci val, !val, MAILBOX_RETRY_US, 8462306a36Sopenharmony_ci MAILBOX_TIMEOUT_US); 8562306a36Sopenharmony_ci if (ret < 0) 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int ret, full; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR, 9662306a36Sopenharmony_ci full, !full, MAILBOX_RETRY_US, 9762306a36Sopenharmony_ci MAILBOX_TIMEOUT_US); 9862306a36Sopenharmony_ci if (ret < 0) 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci writel(val, dp->regs + MAILBOX0_WR_DATA); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, 10762306a36Sopenharmony_ci u8 module_id, u8 opcode, 10862306a36Sopenharmony_ci u16 req_size) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u32 mbox_size, i; 11162306a36Sopenharmony_ci u8 header[4]; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* read the header of the message */ 11562306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 11662306a36Sopenharmony_ci ret = cdn_dp_mailbox_read(dp); 11762306a36Sopenharmony_ci if (ret < 0) 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci header[i] = ret; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci mbox_size = (header[2] << 8) | header[3]; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (opcode != header[0] || module_id != header[1] || 12662306a36Sopenharmony_ci req_size != mbox_size) { 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * If the message in mailbox is not what we want, we need to 12962306a36Sopenharmony_ci * clear the mailbox by reading its contents. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci for (i = 0; i < mbox_size; i++) 13262306a36Sopenharmony_ci if (cdn_dp_mailbox_read(dp) < 0) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, 14262306a36Sopenharmony_ci u8 *buff, u16 buff_size) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 i; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < buff_size; i++) { 14862306a36Sopenharmony_ci ret = cdn_dp_mailbox_read(dp); 14962306a36Sopenharmony_ci if (ret < 0) 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci buff[i] = ret; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, 15962306a36Sopenharmony_ci u8 opcode, u16 size, u8 *message) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci u8 header[4]; 16262306a36Sopenharmony_ci int ret, i; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci header[0] = opcode; 16562306a36Sopenharmony_ci header[1] = module_id; 16662306a36Sopenharmony_ci header[2] = (size >> 8) & 0xff; 16762306a36Sopenharmony_ci header[3] = size & 0xff; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 17062306a36Sopenharmony_ci ret = cdp_dp_mailbox_write(dp, header[i]); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (i = 0; i < size; i++) { 17662306a36Sopenharmony_ci ret = cdp_dp_mailbox_write(dp, message[i]); 17762306a36Sopenharmony_ci if (ret) 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u8 msg[6]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci msg[0] = (addr >> 8) & 0xff; 18962306a36Sopenharmony_ci msg[1] = addr & 0xff; 19062306a36Sopenharmony_ci msg[2] = (val >> 24) & 0xff; 19162306a36Sopenharmony_ci msg[3] = (val >> 16) & 0xff; 19262306a36Sopenharmony_ci msg[4] = (val >> 8) & 0xff; 19362306a36Sopenharmony_ci msg[5] = val & 0xff; 19462306a36Sopenharmony_ci return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER, 19562306a36Sopenharmony_ci sizeof(msg), msg); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, 19962306a36Sopenharmony_ci u8 start_bit, u8 bits_no, u32 val) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci u8 field[8]; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci field[0] = (addr >> 8) & 0xff; 20462306a36Sopenharmony_ci field[1] = addr & 0xff; 20562306a36Sopenharmony_ci field[2] = start_bit; 20662306a36Sopenharmony_ci field[3] = bits_no; 20762306a36Sopenharmony_ci field[4] = (val >> 24) & 0xff; 20862306a36Sopenharmony_ci field[5] = (val >> 16) & 0xff; 20962306a36Sopenharmony_ci field[6] = (val >> 8) & 0xff; 21062306a36Sopenharmony_ci field[7] = val & 0xff; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD, 21362306a36Sopenharmony_ci sizeof(field), field); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciint cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u8 msg[5], reg[5]; 21962306a36Sopenharmony_ci int ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci msg[0] = (len >> 8) & 0xff; 22262306a36Sopenharmony_ci msg[1] = len & 0xff; 22362306a36Sopenharmony_ci msg[2] = (addr >> 16) & 0xff; 22462306a36Sopenharmony_ci msg[3] = (addr >> 8) & 0xff; 22562306a36Sopenharmony_ci msg[4] = addr & 0xff; 22662306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD, 22762306a36Sopenharmony_ci sizeof(msg), msg); 22862306a36Sopenharmony_ci if (ret) 22962306a36Sopenharmony_ci goto err_dpcd_read; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 23262306a36Sopenharmony_ci DPTX_READ_DPCD, 23362306a36Sopenharmony_ci sizeof(reg) + len); 23462306a36Sopenharmony_ci if (ret) 23562306a36Sopenharmony_ci goto err_dpcd_read; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); 23862306a36Sopenharmony_ci if (ret) 23962306a36Sopenharmony_ci goto err_dpcd_read; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, data, len); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cierr_dpcd_read: 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci u8 msg[6], reg[5]; 25062306a36Sopenharmony_ci int ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci msg[0] = 0; 25362306a36Sopenharmony_ci msg[1] = 1; 25462306a36Sopenharmony_ci msg[2] = (addr >> 16) & 0xff; 25562306a36Sopenharmony_ci msg[3] = (addr >> 8) & 0xff; 25662306a36Sopenharmony_ci msg[4] = addr & 0xff; 25762306a36Sopenharmony_ci msg[5] = value; 25862306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD, 25962306a36Sopenharmony_ci sizeof(msg), msg); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci goto err_dpcd_write; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 26462306a36Sopenharmony_ci DPTX_WRITE_DPCD, sizeof(reg)); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci goto err_dpcd_write; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); 26962306a36Sopenharmony_ci if (ret) 27062306a36Sopenharmony_ci goto err_dpcd_write; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) 27362306a36Sopenharmony_ci ret = -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cierr_dpcd_write: 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret); 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciint cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, 28262306a36Sopenharmony_ci u32 i_size, const u32 *d_mem, u32 d_size) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci u32 reg; 28562306a36Sopenharmony_ci int i, ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* reset ucpu before load firmware*/ 28862306a36Sopenharmony_ci writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, 28962306a36Sopenharmony_ci dp->regs + APB_CTRL); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (i = 0; i < i_size; i += 4) 29262306a36Sopenharmony_ci writel(*i_mem++, dp->regs + ADDR_IMEM + i); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci for (i = 0; i < d_size; i += 4) 29562306a36Sopenharmony_ci writel(*d_mem++, dp->regs + ADDR_DMEM + i); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* un-reset ucpu */ 29862306a36Sopenharmony_ci writel(0, dp->regs + APB_CTRL); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* check the keep alive register to make sure fw working */ 30162306a36Sopenharmony_ci ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE, 30262306a36Sopenharmony_ci reg, reg, 2000, FW_ALIVE_TIMEOUT_US); 30362306a36Sopenharmony_ci if (ret < 0) { 30462306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n", 30562306a36Sopenharmony_ci reg); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci reg = readl(dp->regs + VER_L) & 0xff; 31062306a36Sopenharmony_ci dp->fw_version = reg; 31162306a36Sopenharmony_ci reg = readl(dp->regs + VER_H) & 0xff; 31262306a36Sopenharmony_ci dp->fw_version |= reg << 8; 31362306a36Sopenharmony_ci reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff; 31462306a36Sopenharmony_ci dp->fw_version |= reg << 16; 31562306a36Sopenharmony_ci reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff; 31662306a36Sopenharmony_ci dp->fw_version |= reg << 24; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciint cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci u8 msg[5]; 32662306a36Sopenharmony_ci int ret, i; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci msg[0] = GENERAL_MAIN_CONTROL; 32962306a36Sopenharmony_ci msg[1] = MB_MODULE_ID_GENERAL; 33062306a36Sopenharmony_ci msg[2] = 0; 33162306a36Sopenharmony_ci msg[3] = 1; 33262306a36Sopenharmony_ci msg[4] = enable ? FW_ACTIVE : FW_STANDBY; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci for (i = 0; i < sizeof(msg); i++) { 33562306a36Sopenharmony_ci ret = cdp_dp_mailbox_write(dp, msg[i]); 33662306a36Sopenharmony_ci if (ret) 33762306a36Sopenharmony_ci goto err_set_firmware_active; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* read the firmware state */ 34162306a36Sopenharmony_ci for (i = 0; i < sizeof(msg); i++) { 34262306a36Sopenharmony_ci ret = cdn_dp_mailbox_read(dp); 34362306a36Sopenharmony_ci if (ret < 0) 34462306a36Sopenharmony_ci goto err_set_firmware_active; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci msg[i] = ret; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cierr_set_firmware_active: 35262306a36Sopenharmony_ci if (ret < 0) 35362306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "set firmware active failed\n"); 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci u8 msg[8]; 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci msg[0] = CDN_DP_MAX_LINK_RATE; 36362306a36Sopenharmony_ci msg[1] = lanes | SCRAMBLER_EN; 36462306a36Sopenharmony_ci msg[2] = VOLTAGE_LEVEL_2; 36562306a36Sopenharmony_ci msg[3] = PRE_EMPHASIS_LEVEL_3; 36662306a36Sopenharmony_ci msg[4] = PTS1 | PTS2 | PTS3 | PTS4; 36762306a36Sopenharmony_ci msg[5] = FAST_LT_NOT_SUPPORT; 36862306a36Sopenharmony_ci msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; 36962306a36Sopenharmony_ci msg[7] = ENHANCED; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, 37262306a36Sopenharmony_ci DPTX_SET_HOST_CAPABILITIES, 37362306a36Sopenharmony_ci sizeof(msg), msg); 37462306a36Sopenharmony_ci if (ret) 37562306a36Sopenharmony_ci goto err_set_host_cap; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL, 37862306a36Sopenharmony_ci AUX_HOST_INVERT); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cierr_set_host_cap: 38162306a36Sopenharmony_ci if (ret) 38262306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n", ret); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ciint cdn_dp_event_config(struct cdn_dp_device *dp) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci u8 msg[5]; 38962306a36Sopenharmony_ci int ret; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci memset(msg, 0, sizeof(msg)); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT, 39662306a36Sopenharmony_ci sizeof(msg), msg); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n", ret); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciu32 cdn_dp_get_event(struct cdn_dp_device *dp) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci return readl(dp->regs + SW_EVENTS0); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint cdn_dp_get_hpd_status(struct cdn_dp_device *dp) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci u8 status; 41162306a36Sopenharmony_ci int ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE, 41462306a36Sopenharmony_ci 0, NULL); 41562306a36Sopenharmony_ci if (ret) 41662306a36Sopenharmony_ci goto err_get_hpd; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 41962306a36Sopenharmony_ci DPTX_HPD_STATE, sizeof(status)); 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci goto err_get_hpd; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status)); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci goto err_get_hpd; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return status; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cierr_get_hpd: 43062306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n", ret); 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ciint cdn_dp_get_edid_block(void *data, u8 *edid, 43562306a36Sopenharmony_ci unsigned int block, size_t length) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct cdn_dp_device *dp = data; 43862306a36Sopenharmony_ci u8 msg[2], reg[2], i; 43962306a36Sopenharmony_ci int ret; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 44262306a36Sopenharmony_ci msg[0] = block / 2; 44362306a36Sopenharmony_ci msg[1] = block % 2; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID, 44662306a36Sopenharmony_ci sizeof(msg), msg); 44762306a36Sopenharmony_ci if (ret) 44862306a36Sopenharmony_ci continue; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 45162306a36Sopenharmony_ci DPTX_GET_EDID, 45262306a36Sopenharmony_ci sizeof(reg) + length); 45362306a36Sopenharmony_ci if (ret) 45462306a36Sopenharmony_ci continue; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci continue; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, edid, length); 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci continue; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (reg[0] == length && reg[1] == block / 2) 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (ret) 46962306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n", block, 47062306a36Sopenharmony_ci ret); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int cdn_dp_training_start(struct cdn_dp_device *dp) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci unsigned long timeout; 47862306a36Sopenharmony_ci u8 msg, event[2]; 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci msg = LINK_TRAINING_RUN; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* start training */ 48462306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, 48562306a36Sopenharmony_ci sizeof(msg), &msg); 48662306a36Sopenharmony_ci if (ret) 48762306a36Sopenharmony_ci goto err_training_start; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); 49062306a36Sopenharmony_ci while (time_before(jiffies, timeout)) { 49162306a36Sopenharmony_ci msleep(LINK_TRAINING_RETRY_MS); 49262306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, 49362306a36Sopenharmony_ci DPTX_READ_EVENT, 0, NULL); 49462306a36Sopenharmony_ci if (ret) 49562306a36Sopenharmony_ci goto err_training_start; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 49862306a36Sopenharmony_ci DPTX_READ_EVENT, 49962306a36Sopenharmony_ci sizeof(event)); 50062306a36Sopenharmony_ci if (ret) 50162306a36Sopenharmony_ci goto err_training_start; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event)); 50462306a36Sopenharmony_ci if (ret) 50562306a36Sopenharmony_ci goto err_training_start; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (event[1] & EQ_PHASE_FINISHED) 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = -ETIMEDOUT; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cierr_training_start: 51462306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "training failed: %d\n", ret); 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int cdn_dp_get_training_status(struct cdn_dp_device *dp) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci u8 status[10]; 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, 52462306a36Sopenharmony_ci 0, NULL); 52562306a36Sopenharmony_ci if (ret) 52662306a36Sopenharmony_ci goto err_get_training_status; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, 52962306a36Sopenharmony_ci DPTX_READ_LINK_STAT, 53062306a36Sopenharmony_ci sizeof(status)); 53162306a36Sopenharmony_ci if (ret) 53262306a36Sopenharmony_ci goto err_get_training_status; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status)); 53562306a36Sopenharmony_ci if (ret) 53662306a36Sopenharmony_ci goto err_get_training_status; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]); 53962306a36Sopenharmony_ci dp->max_lanes = status[1]; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cierr_get_training_status: 54262306a36Sopenharmony_ci if (ret) 54362306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n", ret); 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciint cdn_dp_train_link(struct cdn_dp_device *dp) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ret = cdn_dp_training_start(dp); 55262306a36Sopenharmony_ci if (ret) { 55362306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret); 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = cdn_dp_get_training_status(dp); 55862306a36Sopenharmony_ci if (ret) { 55962306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret); 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->max_rate, 56462306a36Sopenharmony_ci dp->max_lanes); 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciint cdn_dp_set_video_status(struct cdn_dp_device *dp, int active) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci u8 msg; 57162306a36Sopenharmony_ci int ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci msg = !!active; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, 57662306a36Sopenharmony_ci sizeof(msg), &msg); 57762306a36Sopenharmony_ci if (ret) 57862306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n", ret); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int cdn_dp_get_msa_misc(struct video_info *video, 58462306a36Sopenharmony_ci struct drm_display_mode *mode) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci u32 msa_misc; 58762306a36Sopenharmony_ci u8 val[2] = {0}; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci switch (video->color_fmt) { 59062306a36Sopenharmony_ci case PXL_RGB: 59162306a36Sopenharmony_ci case Y_ONLY: 59262306a36Sopenharmony_ci val[0] = 0; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci /* set YUV default color space conversion to BT601 */ 59562306a36Sopenharmony_ci case YCBCR_4_4_4: 59662306a36Sopenharmony_ci val[0] = 6 + BT_601 * 8; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci case YCBCR_4_2_2: 59962306a36Sopenharmony_ci val[0] = 5 + BT_601 * 8; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case YCBCR_4_2_0: 60262306a36Sopenharmony_ci val[0] = 5; 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci switch (video->color_depth) { 60762306a36Sopenharmony_ci case 6: 60862306a36Sopenharmony_ci val[1] = 0; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci case 8: 61162306a36Sopenharmony_ci val[1] = 1; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci case 10: 61462306a36Sopenharmony_ci val[1] = 2; 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci case 12: 61762306a36Sopenharmony_ci val[1] = 3; 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case 16: 62062306a36Sopenharmony_ci val[1] = 4; 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci msa_misc = 2 * val[0] + 32 * val[1] + 62562306a36Sopenharmony_ci ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return msa_misc; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciint cdn_dp_config_video(struct cdn_dp_device *dp) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct video_info *video = &dp->video_info; 63362306a36Sopenharmony_ci struct drm_display_mode *mode = &dp->mode; 63462306a36Sopenharmony_ci u64 symbol; 63562306a36Sopenharmony_ci u32 val, link_rate, rem; 63662306a36Sopenharmony_ci u8 bit_per_pix, tu_size_reg = TU_SIZE; 63762306a36Sopenharmony_ci int ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? 64062306a36Sopenharmony_ci (video->color_depth * 2) : (video->color_depth * 3); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci link_rate = dp->max_rate / 1000; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); 64562306a36Sopenharmony_ci if (ret) 64662306a36Sopenharmony_ci goto err_config_video; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0); 64962306a36Sopenharmony_ci if (ret) 65062306a36Sopenharmony_ci goto err_config_video; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * get a best tu_size and valid symbol: 65462306a36Sopenharmony_ci * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 65562306a36Sopenharmony_ci * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) 65662306a36Sopenharmony_ci * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set 65762306a36Sopenharmony_ci * TU += 2 and repeat 2nd step. 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci do { 66062306a36Sopenharmony_ci tu_size_reg += 2; 66162306a36Sopenharmony_ci symbol = (u64)tu_size_reg * mode->clock * bit_per_pix; 66262306a36Sopenharmony_ci do_div(symbol, dp->max_lanes * link_rate * 8); 66362306a36Sopenharmony_ci rem = do_div(symbol, 1000); 66462306a36Sopenharmony_ci if (tu_size_reg > 64) { 66562306a36Sopenharmony_ci ret = -EINVAL; 66662306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, 66762306a36Sopenharmony_ci "tu error, clk:%d, lanes:%d, rate:%d\n", 66862306a36Sopenharmony_ci mode->clock, dp->max_lanes, link_rate); 66962306a36Sopenharmony_ci goto err_config_video; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || 67262306a36Sopenharmony_ci (rem > 850) || (rem < 100)); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci val = symbol + (tu_size_reg << 8); 67562306a36Sopenharmony_ci val |= TU_CNT_RST_EN; 67662306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); 67762306a36Sopenharmony_ci if (ret) 67862306a36Sopenharmony_ci goto err_config_video; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* set the FIFO Buffer size */ 68162306a36Sopenharmony_ci val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; 68262306a36Sopenharmony_ci val /= (dp->max_lanes * link_rate); 68362306a36Sopenharmony_ci val = div_u64(8 * (symbol + 1), bit_per_pix) - val; 68462306a36Sopenharmony_ci val += 2; 68562306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci switch (video->color_depth) { 68862306a36Sopenharmony_ci case 6: 68962306a36Sopenharmony_ci val = BCS_6; 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci case 8: 69262306a36Sopenharmony_ci val = BCS_8; 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci case 10: 69562306a36Sopenharmony_ci val = BCS_10; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case 12: 69862306a36Sopenharmony_ci val = BCS_12; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci case 16: 70162306a36Sopenharmony_ci val = BCS_16; 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci val += video->color_fmt << 8; 70662306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val); 70762306a36Sopenharmony_ci if (ret) 70862306a36Sopenharmony_ci goto err_config_video; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; 71162306a36Sopenharmony_ci val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; 71262306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val); 71362306a36Sopenharmony_ci if (ret) 71462306a36Sopenharmony_ci goto err_config_video; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci val = (mode->hsync_start - mode->hdisplay) << 16; 71762306a36Sopenharmony_ci val |= mode->htotal - mode->hsync_end; 71862306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci goto err_config_video; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci val = mode->hdisplay * bit_per_pix / 8; 72362306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val); 72462306a36Sopenharmony_ci if (ret) 72562306a36Sopenharmony_ci goto err_config_video; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); 72862306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val); 72962306a36Sopenharmony_ci if (ret) 73062306a36Sopenharmony_ci goto err_config_video; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci val = mode->hsync_end - mode->hsync_start; 73362306a36Sopenharmony_ci val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); 73462306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val); 73562306a36Sopenharmony_ci if (ret) 73662306a36Sopenharmony_ci goto err_config_video; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci val = mode->vtotal; 73962306a36Sopenharmony_ci val |= (mode->vtotal - mode->vsync_start) << 16; 74062306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val); 74162306a36Sopenharmony_ci if (ret) 74262306a36Sopenharmony_ci goto err_config_video; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci val = mode->vsync_end - mode->vsync_start; 74562306a36Sopenharmony_ci val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); 74662306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val); 74762306a36Sopenharmony_ci if (ret) 74862306a36Sopenharmony_ci goto err_config_video; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci val = cdn_dp_get_msa_misc(video, mode); 75162306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, MSA_MISC, val); 75262306a36Sopenharmony_ci if (ret) 75362306a36Sopenharmony_ci goto err_config_video; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1); 75662306a36Sopenharmony_ci if (ret) 75762306a36Sopenharmony_ci goto err_config_video; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci val = mode->hsync_end - mode->hsync_start; 76062306a36Sopenharmony_ci val |= mode->hdisplay << 16; 76162306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val); 76262306a36Sopenharmony_ci if (ret) 76362306a36Sopenharmony_ci goto err_config_video; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci val = mode->vdisplay; 76662306a36Sopenharmony_ci val |= (mode->vtotal - mode->vsync_start) << 16; 76762306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci goto err_config_video; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci val = mode->vtotal; 77262306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val); 77362306a36Sopenharmony_ci if (ret) 77462306a36Sopenharmony_ci goto err_config_video; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, 0); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cierr_config_video: 77962306a36Sopenharmony_ci if (ret) 78062306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "config video failed: %d\n", ret); 78162306a36Sopenharmony_ci return ret; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ciint cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci int ret; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0); 78962306a36Sopenharmony_ci if (ret) { 79062306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n", ret); 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci writel(0, dp->regs + SPDIF_CTRL_ADDR); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* clearn the audio config and reset */ 79762306a36Sopenharmony_ci writel(0, dp->regs + AUDIO_SRC_CNTL); 79862306a36Sopenharmony_ci writel(0, dp->regs + AUDIO_SRC_CNFG); 79962306a36Sopenharmony_ci writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL); 80062306a36Sopenharmony_ci writel(0, dp->regs + AUDIO_SRC_CNTL); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* reset smpl2pckt component */ 80362306a36Sopenharmony_ci writel(0, dp->regs + SMPL2PKT_CNTL); 80462306a36Sopenharmony_ci writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL); 80562306a36Sopenharmony_ci writel(0, dp->regs + SMPL2PKT_CNTL); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* reset FIFO */ 80862306a36Sopenharmony_ci writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL); 80962306a36Sopenharmony_ci writel(0, dp->regs + FIFO_CNTL); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (audio->format == AFMT_SPDIF) 81262306a36Sopenharmony_ci clk_disable_unprepare(dp->spdif_clk); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ciint cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci int ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable); 82262306a36Sopenharmony_ci if (ret) 82362306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n", ret); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, 82962306a36Sopenharmony_ci struct audio_info *audio) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; 83262306a36Sopenharmony_ci u32 val; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (audio->channels == 2) { 83562306a36Sopenharmony_ci if (dp->max_lanes == 1) 83662306a36Sopenharmony_ci sub_pckt_num = 2; 83762306a36Sopenharmony_ci else 83862306a36Sopenharmony_ci sub_pckt_num = 4; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci i2s_port_en_val = 1; 84162306a36Sopenharmony_ci } else if (audio->channels == 4) { 84262306a36Sopenharmony_ci i2s_port_en_val = 3; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci writel(0x0, dp->regs + SPDIF_CTRL_ADDR); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci val = MAX_NUM_CH(audio->channels); 85062306a36Sopenharmony_ci val |= NUM_OF_I2S_PORTS(audio->channels); 85162306a36Sopenharmony_ci val |= AUDIO_TYPE_LPCM; 85262306a36Sopenharmony_ci val |= CFG_SUB_PCKT_NUM(sub_pckt_num); 85362306a36Sopenharmony_ci writel(val, dp->regs + SMPL2PKT_CNFG); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (audio->sample_width == 16) 85662306a36Sopenharmony_ci val = 0; 85762306a36Sopenharmony_ci else if (audio->sample_width == 24) 85862306a36Sopenharmony_ci val = 1 << 9; 85962306a36Sopenharmony_ci else 86062306a36Sopenharmony_ci val = 2 << 9; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci val |= AUDIO_CH_NUM(audio->channels); 86362306a36Sopenharmony_ci val |= I2S_DEC_PORT_EN(i2s_port_en_val); 86462306a36Sopenharmony_ci val |= TRANS_SMPL_WIDTH_32; 86562306a36Sopenharmony_ci writel(val, dp->regs + AUDIO_SRC_CNFG); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci for (i = 0; i < (audio->channels + 1) / 2; i++) { 86862306a36Sopenharmony_ci if (audio->sample_width == 16) 86962306a36Sopenharmony_ci val = (0x02 << 8) | (0x02 << 20); 87062306a36Sopenharmony_ci else if (audio->sample_width == 24) 87162306a36Sopenharmony_ci val = (0x0b << 8) | (0x0b << 20); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci val |= ((2 * i) << 4) | ((2 * i + 1) << 16); 87462306a36Sopenharmony_ci writel(val, dp->regs + STTS_BIT_CH(i)); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci switch (audio->sample_rate) { 87862306a36Sopenharmony_ci case 32000: 87962306a36Sopenharmony_ci val = SAMPLING_FREQ(3) | 88062306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(0xc); 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case 44100: 88362306a36Sopenharmony_ci val = SAMPLING_FREQ(0) | 88462306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(0xf); 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case 48000: 88762306a36Sopenharmony_ci val = SAMPLING_FREQ(2) | 88862306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(0xd); 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci case 88200: 89162306a36Sopenharmony_ci val = SAMPLING_FREQ(8) | 89262306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(0x7); 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci case 96000: 89562306a36Sopenharmony_ci val = SAMPLING_FREQ(0xa) | 89662306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(5); 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci case 176400: 89962306a36Sopenharmony_ci val = SAMPLING_FREQ(0xc) | 90062306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(3); 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci case 192000: 90362306a36Sopenharmony_ci val = SAMPLING_FREQ(0xe) | 90462306a36Sopenharmony_ci ORIGINAL_SAMP_FREQ(1); 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci val |= 4; 90862306a36Sopenharmony_ci writel(val, dp->regs + COM_CH_STTS_BITS); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); 91162306a36Sopenharmony_ci writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL); 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci u32 val; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); 92162306a36Sopenharmony_ci writel(val, dp->regs + SMPL2PKT_CNFG); 92262306a36Sopenharmony_ci writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; 92562306a36Sopenharmony_ci writel(val, dp->regs + SPDIF_CTRL_ADDR); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci clk_prepare_enable(dp->spdif_clk); 92862306a36Sopenharmony_ci clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciint cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci int ret; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* reset the spdif clk before config */ 93662306a36Sopenharmony_ci if (audio->format == AFMT_SPDIF) { 93762306a36Sopenharmony_ci reset_control_assert(dp->spdif_rst); 93862306a36Sopenharmony_ci reset_control_deassert(dp->spdif_rst); 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC); 94262306a36Sopenharmony_ci if (ret) 94362306a36Sopenharmony_ci goto err_audio_config; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, CM_CTRL, 0); 94662306a36Sopenharmony_ci if (ret) 94762306a36Sopenharmony_ci goto err_audio_config; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (audio->format == AFMT_I2S) 95062306a36Sopenharmony_ci cdn_dp_audio_config_i2s(dp, audio); 95162306a36Sopenharmony_ci else if (audio->format == AFMT_SPDIF) 95262306a36Sopenharmony_ci cdn_dp_audio_config_spdif(dp); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cierr_audio_config: 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret); 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci} 961