Release develop 260205
This commit is contained in:
@@ -254,14 +254,14 @@
|
||||
};
|
||||
|
||||
&ao_gpio0 {
|
||||
pwoer-5v-en-hog {
|
||||
power-5v-en-hog {
|
||||
gpio-hog;
|
||||
gpios = <29 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "power_5v_en";
|
||||
};
|
||||
|
||||
pwoer-3v3-en-hog {
|
||||
power-3v3-en-hog {
|
||||
gpio-hog;
|
||||
gpios = <26 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
@@ -448,10 +448,10 @@
|
||||
pins = "AOI2C1_SCL", "AOI2C1_SDA";
|
||||
function = "aoi2c1";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
usbc0_int: usbc0-int {
|
||||
@@ -665,10 +665,10 @@
|
||||
pins = "GPIO2_28", "GPIO2_29";
|
||||
function = "i2c5";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
uart5_pins: uart5-1 {
|
||||
@@ -697,10 +697,10 @@
|
||||
pins = "GPIO2_8", "GPIO2_9";
|
||||
function = "i2c6";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
i2s1_pins: i2s1-0 {
|
||||
@@ -718,10 +718,10 @@
|
||||
pins = "GPIO2_10", "GPIO2_11";
|
||||
function = "i2c7";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
i2c4_pins: i2c4-2 {
|
||||
@@ -729,10 +729,10 @@
|
||||
pins = "GPIO2_26", "GPIO2_27";
|
||||
function = "i2c4";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
pwm1_pins: pwm1-1 {
|
||||
@@ -1089,6 +1089,13 @@
|
||||
remote-endpoint = <&usbdp_phy0_orientation_switch>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dp_altmode_mux: endpoint {
|
||||
remote-endpoint = <&usbdp_phy0_dp_altmode_mux>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1330,10 +1337,18 @@
|
||||
};
|
||||
};
|
||||
|
||||
&dp0 {
|
||||
force-hpd = <1>;
|
||||
extcon = <&usb31_c10phy>;
|
||||
};
|
||||
|
||||
/* USB3.1/DP Combo PHY0 */
|
||||
&usb31_c10phy {
|
||||
orientation-switch;
|
||||
mode-switch;
|
||||
status = "okay";
|
||||
svid = <0xff01>;
|
||||
#extcon-cells = <0>;
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
@@ -1342,5 +1357,10 @@
|
||||
reg = <0>;
|
||||
remote-endpoint = <&usbc0_orien_sw>;
|
||||
};
|
||||
|
||||
usbdp_phy0_dp_altmode_mux: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&dp_altmode_mux>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -410,10 +410,10 @@
|
||||
pins = "GPIO0_22", "GPIO0_23";
|
||||
function = "i2c2";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
tdm_pins: tdm-0 {
|
||||
@@ -421,7 +421,7 @@
|
||||
pins = "GPIO0_19", "GPIO0_20", "GPIO0_21";
|
||||
function = "tdm";
|
||||
bias-disable;
|
||||
drive-strength = <15>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
@@ -432,10 +432,10 @@
|
||||
pins = "GPIO0_24", "GPIO0_25";
|
||||
function = "i2c0";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
can0_pins: can0-0 {
|
||||
@@ -464,10 +464,10 @@
|
||||
pins = "GPIO0_26", "GPIO0_27";
|
||||
function = "i2c1";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
can1_pins: can1-0 {
|
||||
@@ -657,10 +657,10 @@
|
||||
pins = "GPIO2_2", "GPIO2_3";
|
||||
function = "i2c5";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
uart5_pins: uart5-0 {
|
||||
@@ -689,10 +689,10 @@
|
||||
pins = "GPIO2_4", "GPIO2_5";
|
||||
function = "i2c6";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
uart6_pins: uart6-0 {
|
||||
@@ -784,10 +784,10 @@
|
||||
// pins = "GPIO2_6", "GPIO2_7";
|
||||
// function = "i2c4";
|
||||
// bias-disable;
|
||||
// drive-strength = <7>;
|
||||
// drive-strength = <25>;
|
||||
// input-enable;
|
||||
// input-schmitt-enable;
|
||||
// slew-rate = <0>;
|
||||
// slew-rate = <1>;
|
||||
// };
|
||||
// };
|
||||
i2s2_pins: i2s2-0 {
|
||||
@@ -826,10 +826,10 @@
|
||||
pins = "GPIO2_10", "GPIO2_11";
|
||||
function = "i2c7";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
spi1_pins: spi1-1 {
|
||||
@@ -859,10 +859,10 @@
|
||||
pins = "GPIO2_26", "GPIO2_27";
|
||||
function = "i2c4";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
pwm1_pins: pwm1-1 {
|
||||
@@ -881,10 +881,10 @@
|
||||
pins = "GPIO2_24", "GPIO2_25";
|
||||
function = "i2c3";
|
||||
bias-disable;
|
||||
drive-strength = <7>;
|
||||
drive-strength = <25>;
|
||||
input-enable;
|
||||
input-schmitt-enable;
|
||||
slew-rate = <0>;
|
||||
slew-rate = <1>;
|
||||
};
|
||||
};
|
||||
hdmi_pins: hdmi-0 {
|
||||
@@ -1730,3 +1730,7 @@
|
||||
opp-microvolt = <1000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&dp0 {
|
||||
force-hpd = <1>;
|
||||
};
|
||||
|
||||
@@ -203,8 +203,8 @@
|
||||
<&clk_usb DPTX_PCLK_EN>;
|
||||
clock-names = "i2s", "ipi", "aux", "gtc", "pclk";
|
||||
power-domains = <&power_vo>;
|
||||
force-hpd = <1>;
|
||||
#sound-dai-cells = <1>;
|
||||
phys = <&usbdp_phy0_dp>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@@ -1397,6 +1397,16 @@
|
||||
reset-names = "phy-rst";
|
||||
#phy-cells = <0>;
|
||||
power-domains = <&power_usb>;
|
||||
|
||||
usbdp_phy0_dp: dp-port {
|
||||
#phy-cells = <0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usbdp_phy0_u3: u3-port {
|
||||
#phy-cells = <0>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
usb20phy0: phy@08300000 {
|
||||
@@ -1460,7 +1470,7 @@
|
||||
reg-io-width = <4>;
|
||||
dr_mode = "host";
|
||||
power-domains = <&power_usb>;
|
||||
phys = <&usb20phy2>, <&usb31_c10phy>;
|
||||
phys = <&usb20phy2>, <&usbdp_phy0_u3>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
//iommus = <&iommu DEVID_DIE0_USB3_0>;
|
||||
snps,usb3_lpm_capable;
|
||||
|
||||
@@ -342,6 +342,7 @@ CONFIG_V4L_MEM2MEM_DRIVERS=y
|
||||
CONFIG_DRM=y
|
||||
CONFIG_DRM_LOAD_EDID_FIRMWARE=y
|
||||
CONFIG_DRM_DP_AUX_CHARDEV=y
|
||||
CONFIG_TYPEC_DP_ALTMODE=m
|
||||
CONFIG_DRM_PANEL_SIMPLE=m
|
||||
CONFIG_DRM_PANEL_ILITEK_ILI9881C=m
|
||||
CONFIG_DRM_PANEL_JADARD_JD9365DA_H3=y
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_device.h>
|
||||
@@ -47,9 +48,6 @@
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include <uapi/linux/videodev2.h>
|
||||
#define NO_PHY 1
|
||||
#define MAX_FREQ 540000
|
||||
#define LANES_NUM 4
|
||||
|
||||
#define RK_IF_PROP_COLOR_DEPTH "color_depth"
|
||||
#define RK_IF_PROP_COLOR_FORMAT "color_format"
|
||||
@@ -561,7 +559,9 @@ struct dw_dp {
|
||||
bool force_hpd;
|
||||
struct dw_dp_hotplug hotplug;
|
||||
struct mutex irq_lock;
|
||||
struct extcon_dev *extcon;
|
||||
struct extcon_dev *extcon; /* Extcon provider for audio/userspace */
|
||||
struct extcon_dev *phy_extcon; /* Extcon consumer from PHY */
|
||||
struct notifier_block phy_extcon_nb; /* Notifier for PHY extcon events */
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
@@ -696,15 +696,6 @@ static const struct dw_dp_output_format possible_output_fmts[] = {
|
||||
DPTX_VM_RGB_6BIT, 6, 18 },
|
||||
};
|
||||
|
||||
static unsigned long g_init_threshold = 0;
|
||||
static unsigned long g_freqs = 0;
|
||||
//void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev)
|
||||
//{
|
||||
// mutex_lock(&rockchip_drm_sub_dev_lock);
|
||||
// list_add_tail(&sub_dev->list, &rockchip_drm_sub_dev_list);
|
||||
// mutex_unlock(&rockchip_drm_sub_dev_lock);
|
||||
//}
|
||||
|
||||
uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev,
|
||||
struct device_node *port)
|
||||
{
|
||||
@@ -884,7 +875,7 @@ static int dw_dp_hdcp2_auth_check(struct dw_dp *dp)
|
||||
|
||||
dp->hdcp.hdcp2_encrypted = true;
|
||||
|
||||
dev_err(dp->dev, "HDCP2 authentication succeed\n");
|
||||
dev_dbg(dp->dev, "HDCP2 authentication succeed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -961,7 +952,7 @@ static int __maybe_unused dw_dp_hdcp_auth_check(struct dw_dp *dp)
|
||||
} else {
|
||||
regmap_write(dp->regmap, DPTX_HDCPAPIINTCLR, HDCP_ENGAGED);
|
||||
dp->hdcp.hdcp_encrypted = true;
|
||||
dev_err(dp->dev, "HDCP authentication succeed\n");
|
||||
dev_dbg(dp->dev, "HDCP authentication succeed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1611,7 +1602,7 @@ static int dw_dp_connector_atomic_check(struct drm_connector *conn,
|
||||
dp_old_state = connector_to_dp_state(old_state);
|
||||
dp_new_state = connector_to_dp_state(new_state);
|
||||
|
||||
//dw_dp_hdcp_atomic_check(conn, state);
|
||||
dw_dp_hdcp_atomic_check(conn, state);
|
||||
|
||||
if (!new_state->crtc)
|
||||
return 0;
|
||||
@@ -1636,7 +1627,7 @@ static int dw_dp_connector_atomic_check(struct drm_connector *conn,
|
||||
if ((dp_old_state->bpc != dp_new_state->bpc) ||
|
||||
(dp_old_state->color_format != dp_new_state->color_format)) {
|
||||
if ((dp_old_state->bpc == 0) && (dp_new_state->bpc == 0))
|
||||
dev_err(dp->dev, "still auto set color mode\n");
|
||||
dev_dbg(dp->dev, "still auto set color mode\n");
|
||||
else
|
||||
crtc_state->mode_changed = true;
|
||||
}
|
||||
@@ -1758,18 +1749,12 @@ static int dw_dp_link_probe(struct dw_dp *dp)
|
||||
!!(dpcd & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED);
|
||||
|
||||
link->revision = link->dpcd[DP_DPCD_REV];
|
||||
#if NO_PHY
|
||||
//link->rate = MAX_FREQ;
|
||||
//link->lanes = LANES_NUM;
|
||||
link->rate = drm_dp_max_link_rate(link->dpcd);
|
||||
link->lanes = min_t(u8, LANES_NUM, drm_dp_max_lane_count(link->dpcd));
|
||||
#else
|
||||
link->rate = min_t(u32, min(dp->max_link_rate, dp->phy->attrs.max_link_rate * 100),
|
||||
drm_dp_max_link_rate(link->dpcd));
|
||||
|
||||
link->lanes = min_t(u8, phy_get_bus_width(dp->phy),
|
||||
drm_dp_max_lane_count(link->dpcd));
|
||||
#endif
|
||||
|
||||
link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(link->dpcd);
|
||||
link->caps.tps3_supported = drm_dp_tps3_supported(link->dpcd);
|
||||
link->caps.tps4_supported = drm_dp_tps4_supported(link->dpcd);
|
||||
@@ -1786,9 +1771,7 @@ static int dw_dphy_set_voltage_pre_level(struct dw_dp *dp, int lane_id, int leve
|
||||
static int dw_dp_phy_update_vs_emph(struct dw_dp *dp, unsigned int rate, unsigned int lanes,
|
||||
struct drm_dp_link_train_set *train_set)
|
||||
{
|
||||
#if !NO_PHY
|
||||
union phy_configure_opts phy_cfg;
|
||||
#endif
|
||||
unsigned int *vs, *pe;
|
||||
u8 buf[4];
|
||||
int i, ret;
|
||||
@@ -1796,19 +1779,11 @@ static int dw_dp_phy_update_vs_emph(struct dw_dp *dp, unsigned int rate, unsigne
|
||||
vs = train_set->voltage_swing;
|
||||
pe = train_set->pre_emphasis;
|
||||
|
||||
// unsigned int phy_ctl_val;
|
||||
// regmap_read(dp->regmap, DPTX_PHYIF_CTRL, &phy_ctl_val);
|
||||
// regmap_update_bits(dp->regmap, DPTX_PHYIF_CTRL, PHY_POWERDOWN,
|
||||
// FIELD_PREP(PHY_POWERDOWN, 0x3));
|
||||
//
|
||||
for (i = 0; i < lanes; i++) {
|
||||
dw_dphy_set_voltage_sw_level(dp, i, vs[i]);
|
||||
dw_dphy_set_voltage_pre_level(dp, i, pe[i]);
|
||||
}
|
||||
|
||||
// regmap_update_bits(dp->regmap, DPTX_PHYIF_CTRL, PHY_POWERDOWN,
|
||||
// FIELD_PREP(PHY_POWERDOWN, 0x0));
|
||||
#if !NO_PHY
|
||||
for (i = 0; i < lanes; i++) {
|
||||
phy_cfg.dp.voltage[i] = vs[i];
|
||||
phy_cfg.dp.pre[i] = pe[i];
|
||||
@@ -1821,9 +1796,9 @@ static int dw_dp_phy_update_vs_emph(struct dw_dp *dp, unsigned int rate, unsigne
|
||||
phy_cfg.dp.set_voltages = true;
|
||||
|
||||
ret = phy_configure(dp->phy, &phy_cfg);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < lanes; i++) {
|
||||
buf[i] = (vs[i] << DP_TRAIN_VOLTAGE_SWING_SHIFT) |
|
||||
@@ -1866,7 +1841,7 @@ static int dw_dphy_set_rate(struct dw_dp *dp, unsigned int rate)
|
||||
val = 3;
|
||||
break;
|
||||
default:
|
||||
dev_err(dp->dev, "%s, %d, Error, dp unsopport rate %u\n", __func__, __LINE__, rate);
|
||||
dev_err(dp->dev, "Error, dp unsopport rate %u\n", rate);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1877,7 +1852,6 @@ static int dw_dphy_set_rate(struct dw_dp *dp, unsigned int rate)
|
||||
static int dw_dphy_set_voltage_sw_level(struct dw_dp *dp, int lane_id, int level)
|
||||
{
|
||||
if (level < 0 || level > 3) {
|
||||
dev_err(dp->dev, "%s, %d, Error, dp sw unsopport level %d\n", __func__, __LINE__, level);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1897,7 +1871,7 @@ static int dw_dphy_set_voltage_sw_level(struct dw_dp *dp, int lane_id, int level
|
||||
static int dw_dphy_set_voltage_pre_level(struct dw_dp *dp, int lane_id, int level)
|
||||
{
|
||||
if (level < 0 || level > 3) {
|
||||
dev_err(dp->dev, "%s, %d, Error, dp pre unsopport level%d\n", __func__, __LINE__, level);
|
||||
dev_err(dp->dev, "Error, dp pre unsopport level%d\n", level);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1918,25 +1892,11 @@ static int dw_dp_phy_configure(struct dw_dp *dp, unsigned int rate,
|
||||
unsigned int lanes, bool ssc)
|
||||
{
|
||||
/* Move PHY to P3 */
|
||||
unsigned int phy_ctl_val = 0, count = 10;
|
||||
regmap_update_bits(dp->regmap, DPTX_PHYIF_CTRL, PHY_POWERDOWN,
|
||||
FIELD_PREP(PHY_POWERDOWN, 0x3));
|
||||
|
||||
regmap_read(dp->regmap, DPTX_PHYIF_CTRL, &phy_ctl_val);
|
||||
while((PHY_BUSY & phy_ctl_val) && count) {
|
||||
regmap_read(dp->regmap, DPTX_PHYIF_CTRL, &phy_ctl_val);
|
||||
count--;
|
||||
dev_dbg(dp->dev, "dp wait phy busy 0x%lx\n", (PHY_BUSY & phy_ctl_val));
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (PHY_BUSY & phy_ctl_val) {
|
||||
dev_err(dp->dev, "%s, %d, Error, phy is busy\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
dw_dphy_set_rate(dp, rate);
|
||||
|
||||
#if !NO_PHY
|
||||
union phy_configure_opts phy_cfg;
|
||||
int ret;
|
||||
phy_cfg.dp.lanes = lanes;
|
||||
@@ -1948,7 +1908,6 @@ static int dw_dp_phy_configure(struct dw_dp *dp, unsigned int rate,
|
||||
ret = phy_configure(dp->phy, &phy_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
regmap_update_bits(dp->regmap, DPTX_PHYIF_CTRL, PHY_LANES,
|
||||
FIELD_PREP(PHY_LANES, lanes / 2));
|
||||
@@ -2065,7 +2024,6 @@ static int dw_dp_link_train_set_pattern(struct dw_dp *dp, u32 pattern)
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
buf | pattern);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "%s, %d, dpct write error %d\n", __func__, __LINE__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2237,6 +2195,7 @@ static int dw_dp_link_downgrade(struct dw_dp *dp)
|
||||
{
|
||||
struct dw_dp_link *link = &dp->link;
|
||||
struct dw_dp_video *video = &dp->video;
|
||||
u32 rate = link->rate;
|
||||
|
||||
switch (link->rate) {
|
||||
case 162000:
|
||||
@@ -2252,9 +2211,11 @@ static int dw_dp_link_downgrade(struct dw_dp *dp)
|
||||
break;
|
||||
}
|
||||
|
||||
/*If the downgrade fails, retry using the previous rate. */
|
||||
if (!dw_dp_bandwidth_ok(dp, &video->mode, video->bpp, link->lanes,
|
||||
link->rate))
|
||||
return -E2BIG;
|
||||
link->rate)) {
|
||||
link->rate = rate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2266,6 +2227,10 @@ static int dw_dp_link_train_full(struct dw_dp *dp)
|
||||
|
||||
retry:
|
||||
dw_dp_link_train_init(&link->train);
|
||||
|
||||
dev_dbg(dp->dev, "full-training link: %u lane%s at %u MHz\n",
|
||||
link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100);
|
||||
|
||||
ret = dw_dp_link_configure(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to configure DP link: %d\n", ret);
|
||||
@@ -2279,11 +2244,10 @@ retry:
|
||||
}
|
||||
|
||||
if (!link->train.clock_recovered) {
|
||||
dev_err(dp->dev, "clock recovery failed, downgrading link\n");
|
||||
dev_info(dp->dev, "clock recovery failed, downgrading link, retry\n");
|
||||
|
||||
ret = dw_dp_link_downgrade(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "downgrade failed, goto out\n");
|
||||
goto out;
|
||||
} else
|
||||
goto retry;
|
||||
@@ -2318,6 +2282,9 @@ static int dw_dp_link_train_fast(struct dw_dp *dp)
|
||||
|
||||
dw_dp_link_train_init(&link->train);
|
||||
|
||||
dev_dbg(dp->dev, "fast-training link: %u lane%s at %u MHz\n",
|
||||
link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100);
|
||||
|
||||
ret = dw_dp_link_configure(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to configure DP link: %d\n", ret);
|
||||
@@ -2664,12 +2631,11 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
u8 color_format = video->color_format;
|
||||
u8 bpc = video->bpc;
|
||||
u8 pixel_mode = video->pixel_mode;
|
||||
u8 bpp = video->bpp, vic;
|
||||
u32 init_threshold;
|
||||
u8 bpp = video->bpp, init_threshold, vic;
|
||||
u32 hactive, hblank, h_sync_width, h_front_porch;
|
||||
u32 vactive, vblank, v_sync_width, v_front_porch;
|
||||
u32 vstart = mode->vsync_end- mode->vdisplay;
|
||||
u32 hstart = mode->hsync_end- mode->hdisplay;
|
||||
u32 vstart = mode->vtotal - mode->vsync_start;
|
||||
u32 hstart = mode->htotal - mode->hsync_start;
|
||||
u32 peak_stream_bandwidth, link_bandwidth;
|
||||
u32 average_bytes_per_tu, average_bytes_per_tu_frac;
|
||||
u32 ts, hblank_interval;
|
||||
@@ -2696,7 +2662,6 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
regmap_write(dp->regmap, DPTX_VINPUT_POLARITY_CTRL, value);
|
||||
|
||||
/* Configure DPTX_VIDEO_CONFIG1 register */
|
||||
|
||||
hactive = mode->hdisplay;
|
||||
hblank = mode->htotal - mode->hdisplay;
|
||||
value = FIELD_PREP(HACTIVE, hactive) | FIELD_PREP(HBLANK, hblank);
|
||||
@@ -2716,7 +2681,6 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
/* Configure DPTX_VIDEO_CONFIG2 register */
|
||||
vblank = mode->vtotal - mode->vdisplay;
|
||||
vactive = mode->vdisplay;
|
||||
|
||||
regmap_write(dp->regmap, DPTX_VIDEO_CONFIG2,
|
||||
FIELD_PREP(VBLANK, vblank) | FIELD_PREP(VACTIVE, vactive));
|
||||
|
||||
@@ -2733,16 +2697,15 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
regmap_write(dp->regmap, DPTX_VIDEO_CONFIG4,
|
||||
FIELD_PREP(V_SYNC_WIDTH, v_sync_width) |
|
||||
FIELD_PREP(V_FRONT_PORCH, v_front_porch));
|
||||
|
||||
/* Configure DPTX_VIDEO_CONFIG5 register */
|
||||
peak_stream_bandwidth = mode->clock * bpp / 8;
|
||||
link_bandwidth = (link->rate / 1000) * link->lanes;
|
||||
ts = peak_stream_bandwidth * 64 / link_bandwidth;
|
||||
average_bytes_per_tu = ts / 1000;
|
||||
average_bytes_per_tu_frac = ts - average_bytes_per_tu * 1000;
|
||||
average_bytes_per_tu_frac = average_bytes_per_tu_frac / 100;
|
||||
|
||||
average_bytes_per_tu_frac = ts / 100 - average_bytes_per_tu * 10;
|
||||
if (pixel_mode == DPTX_MP_SINGLE_PIXEL) {
|
||||
if (average_bytes_per_tu < 16)
|
||||
if (average_bytes_per_tu < 6)
|
||||
init_threshold = 32;
|
||||
else if (hblank <= 80 && color_format != DRM_COLOR_FORMAT_YCBCR420)
|
||||
init_threshold = 12;
|
||||
@@ -2750,13 +2713,64 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
init_threshold = 3;
|
||||
else
|
||||
init_threshold = 16;
|
||||
} else {
|
||||
u32 t1 = 0, t2 = 0, t3 = 0;
|
||||
|
||||
switch (bpc) {
|
||||
case 6:
|
||||
t1 = (4 * 1000 / 9) * link->lanes;
|
||||
break;
|
||||
case 8:
|
||||
if (color_format == DRM_COLOR_FORMAT_YCBCR422) {
|
||||
t1 = (1000 / 2) * link->lanes;
|
||||
} else {
|
||||
if (pixel_mode == DPTX_MP_DUAL_PIXEL)
|
||||
t1 = (1000 / 3) * link->lanes;
|
||||
else
|
||||
t1 = (3000 / 16) * link->lanes;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (color_format == DRM_COLOR_FORMAT_YCBCR422)
|
||||
t1 = (2000 / 5) * link->lanes;
|
||||
else
|
||||
t1 = (4000 / 15) * link->lanes;
|
||||
break;
|
||||
case 12:
|
||||
if (color_format == DRM_COLOR_FORMAT_YCBCR422) {
|
||||
if (pixel_mode == DPTX_MP_DUAL_PIXEL)
|
||||
t1 = (1000 / 6) * link->lanes;
|
||||
else
|
||||
t1 = (1000 / 3) * link->lanes;
|
||||
} else {
|
||||
t1 = (2000 / 9) * link->lanes;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (color_format != DRM_COLOR_FORMAT_YCBCR422 &&
|
||||
pixel_mode == DPTX_MP_DUAL_PIXEL)
|
||||
t1 = (1000 / 6) * link->lanes;
|
||||
else
|
||||
t1 = (1000 / 4) * link->lanes;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (color_format == DRM_COLOR_FORMAT_YCBCR420)
|
||||
t2 = (link->rate / 4) * 1000 / (mode->clock / 2);
|
||||
else
|
||||
t2 = (link->rate / 4) * 1000 / mode->clock;
|
||||
|
||||
if (average_bytes_per_tu_frac)
|
||||
t3 = average_bytes_per_tu + 1;
|
||||
else
|
||||
t3 = average_bytes_per_tu;
|
||||
init_threshold = t1 * t2 * t3 / (1000 * 1000);
|
||||
if (init_threshold <= 16 || average_bytes_per_tu < 10)
|
||||
init_threshold = 40;
|
||||
}
|
||||
|
||||
if (g_init_threshold) {
|
||||
init_threshold = g_init_threshold;
|
||||
}
|
||||
|
||||
average_bytes_per_tu_frac &= 0xf;
|
||||
regmap_write(dp->regmap, DPTX_VIDEO_CONFIG5,
|
||||
FIELD_PREP(INIT_THRESHOLD_HI, init_threshold >> 6) |
|
||||
FIELD_PREP(AVERAGE_BYTES_PER_TU_FRAC, average_bytes_per_tu_frac) |
|
||||
@@ -2766,6 +2780,7 @@ static int dw_dp_video_enable(struct dw_dp *dp)
|
||||
/* Configure DPTX_VIDEO_HBLANK_INTERVAL register */
|
||||
hblank_interval = hblank * (link->rate / 4) / mode->clock;
|
||||
regmap_write(dp->regmap, DPTX_VIDEO_HBLANK_INTERVAL,
|
||||
FIELD_PREP(HBLANK_INTERVAL_EN, 1) |
|
||||
FIELD_PREP(HBLANK_INTERVAL, hblank_interval));
|
||||
|
||||
/* Video stream enable */
|
||||
@@ -2807,6 +2822,63 @@ static irqreturn_t dw_dp_hpd_irq_handler(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
┌────────────────────┬───────────┬─────────┬────────────────┐
|
||||
│ Scenario │ HPD_STATE │ IRQ_HPD │ DP Host Action │
|
||||
├────────────────────┼───────────┼─────────┼────────────────┤
|
||||
│ Display insertion │ 0→1 │ 0 │ Long HPD │
|
||||
├────────────────────┼───────────┼─────────┼────────────────┤
|
||||
│ Display removal │ 1→0 │ 0 │ Long HPD │
|
||||
├────────────────────┼───────────┼─────────┼────────────────┤
|
||||
│ Link status change │ 1 │ 1 │ Short HPD │
|
||||
├────────────────────┼───────────┼─────────┼────────────────┤
|
||||
│ EDID update │ 1 │ 1 │ Short HPD │
|
||||
├────────────────────┼───────────┼─────────┼────────────────┤
|
||||
│ Resolution switch │ 1 │ 1 │ Short HPD │
|
||||
└────────────────────┴───────────┴─────────┴────────────────┘
|
||||
*/
|
||||
static int dw_dp_phy_extcon_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dw_dp *dp = container_of(nb, struct dw_dp, phy_extcon_nb);
|
||||
bool hpd_state = !!event;
|
||||
bool current_hpd;
|
||||
|
||||
dev_dbg(dp->dev, "PHY extcon HPD notification: %s (event=%lu)\n",
|
||||
hpd_state ? "connected" : "disconnected", event);
|
||||
|
||||
mutex_lock(&dp->irq_lock);
|
||||
|
||||
/* Get current HPD status */
|
||||
current_hpd = dp->hotplug.status;
|
||||
|
||||
/*
|
||||
* Distinguish between long HPD and short HPD:
|
||||
* - If state changes → long HPD (plug/unplug)
|
||||
* - If state unchanged and state=true → short HPD (IRQ pulse)
|
||||
*/
|
||||
if (current_hpd == hpd_state && hpd_state) {
|
||||
/* State unchanged → short HPD */
|
||||
dp->hotplug.long_hpd = false;
|
||||
dev_dbg(dp->dev, "Short HPD detected (IRQ pulse)\n");
|
||||
if (!current_hpd)
|
||||
return NOTIFY_OK;
|
||||
} else {
|
||||
/* State changed → long HPD */
|
||||
dp->hotplug.long_hpd = true;
|
||||
dp->hotplug.status = hpd_state;
|
||||
dev_dbg(dp->dev, "Long HPD detected: %s\n",
|
||||
hpd_state ? "plug" : "unplug");
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->irq_lock);
|
||||
|
||||
/* Schedule HPD work queue */
|
||||
schedule_work(&dp->hpd_work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void dw_dp_hpd_init(struct dw_dp *dp)
|
||||
{
|
||||
dp->hotplug.status = dw_dp_detect(dp);
|
||||
@@ -2841,13 +2913,6 @@ static void dw_dp_init(struct dw_dp *dp)
|
||||
regmap_update_bits(dp->regmap, DPTX_CCTL, DEFAULT_FAST_LINK_TRAIN_EN,
|
||||
FIELD_PREP(DEFAULT_FAST_LINK_TRAIN_EN, 0));
|
||||
|
||||
#if 0
|
||||
regmap_update_bits(dp->regmap, DPTX_GENERAL_INTERRUPT_ENABLE,
|
||||
VIDEO_FIFO_OVERFLOW_STREAM0, FIELD_PREP(VIDEO_FIFO_OVERFLOW_STREAM0, 1));
|
||||
|
||||
regmap_update_bits(dp->regmap, DPTX_GENERAL_INTERRUPT_ENABLE,
|
||||
VIDEO_FIFO_UNDERFLOW_STREAM0, FIELD_PREP(VIDEO_FIFO_UNDERFLOW_STREAM0, 1));
|
||||
#endif
|
||||
dw_dp_hpd_init(dp);
|
||||
dw_dp_aux_init(dp);
|
||||
}
|
||||
@@ -2991,7 +3056,6 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
|
||||
case DP_AUX_I2C_READ:
|
||||
break;
|
||||
default: {
|
||||
dev_err(dp->dev, "%s, %d, aux error\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -3114,13 +3178,9 @@ static void _dw_dp_loader_protect(struct dw_dp *dp, bool on)
|
||||
link->rate = 162000;
|
||||
break;
|
||||
}
|
||||
#if !NO_PHY
|
||||
phy_power_on(dp->phy);
|
||||
#endif
|
||||
} else {
|
||||
#if !NO_PHY
|
||||
phy_power_off(dp->phy);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3350,9 +3410,7 @@ static void dw_dp_link_disable(struct dw_dp *dp)
|
||||
|
||||
dw_dp_phy_xmit_enable(dp, 0);
|
||||
|
||||
#if !NO_PHY
|
||||
phy_power_off(dp->phy);
|
||||
#endif
|
||||
|
||||
link->train.clock_recovered = false;
|
||||
link->train.channel_equalized = false;
|
||||
@@ -3362,21 +3420,19 @@ static int dw_dp_link_enable(struct dw_dp *dp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if !NO_PHY
|
||||
ret = phy_power_on(dp->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
ret = dw_dp_link_power_up(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "dw_dp_link_power_up failed: %d\n", ret);
|
||||
dev_info(dp->dev, "dw_dp_link_power_up failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dw_dp_link_train(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "link training failed: %d\n", ret);
|
||||
dev_info(dp->dev, "link training failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3409,7 +3465,7 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
|
||||
ret = dw_dp_link_enable(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to enable link: %d\n", ret);
|
||||
dev_info(dp->dev, "failed to enable link: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3473,11 +3529,9 @@ static bool dw_dp_detect_dpcd(struct dw_dp *dp)
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
#if !NO_PHY
|
||||
ret = phy_power_on(dp->phy);
|
||||
if (ret)
|
||||
goto fail_power_on;
|
||||
#endif
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_DPCD_REV, &value);
|
||||
if (ret < 0) {
|
||||
goto fail_probe;
|
||||
@@ -3489,17 +3543,13 @@ static bool dw_dp_detect_dpcd(struct dw_dp *dp)
|
||||
goto fail_probe;
|
||||
}
|
||||
|
||||
#if !NO_PHY
|
||||
phy_power_off(dp->phy);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
fail_probe:
|
||||
#if !NO_PHY
|
||||
phy_power_off(dp->phy);
|
||||
fail_power_on:
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3540,17 +3590,13 @@ static struct edid *dw_dp_bridge_get_edid(struct drm_bridge *bridge,
|
||||
{
|
||||
struct dw_dp *dp = bridge_to_dp(bridge);
|
||||
struct edid *edid;
|
||||
#if !NO_PHY
|
||||
int ret;
|
||||
|
||||
ret = phy_power_on(dp->phy);
|
||||
if (ret)
|
||||
return NULL;
|
||||
#endif
|
||||
edid = drm_get_edid(connector, &dp->aux.ddc);
|
||||
#if !NO_PHY
|
||||
phy_power_off(dp->phy);
|
||||
#endif
|
||||
|
||||
return edid;
|
||||
}
|
||||
@@ -3972,18 +4018,6 @@ static irqreturn_t dw_dp_irq_handler(int irq, void *data)
|
||||
if (!value)
|
||||
return IRQ_NONE;
|
||||
|
||||
#if 0
|
||||
if (value & VIDEO_FIFO_OVERFLOW_STREAM0) {
|
||||
regmap_write(dp->regmap, DPTX_GENERAL_INTERRUPT,
|
||||
VIDEO_FIFO_OVERFLOW_STREAM0);
|
||||
}
|
||||
|
||||
if (value & VIDEO_FIFO_UNDERFLOW_STREAM0) {
|
||||
regmap_write(dp->regmap, DPTX_GENERAL_INTERRUPT,
|
||||
VIDEO_FIFO_UNDERFLOW_STREAM0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (value & HPD_EVENT)
|
||||
dw_dp_handle_hpd_event(dp);
|
||||
|
||||
@@ -3996,8 +4030,6 @@ static irqreturn_t dw_dp_irq_handler(int irq, void *data)
|
||||
if (value & HDCP_EVENT)
|
||||
dw_dp_handle_hdcp_event(dp);
|
||||
|
||||
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -4355,87 +4387,13 @@ static int dw_dp_parse_dt(struct dw_dp *dp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dpinit_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
char kbuf[64];
|
||||
char *p, *token;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
struct dw_dp *dp = dev_get_drvdata(dev);
|
||||
struct dw_dp_link *link = &dp->link;
|
||||
|
||||
|
||||
/* 简单的长度检查,避免溢出 */
|
||||
if (count == 0)
|
||||
return -EINVAL;
|
||||
if (count >= sizeof(kbuf))
|
||||
return -EINVAL;
|
||||
|
||||
/* buf 是 sysfs 传入的 kernel 空间缓存(已可读),复制并 null 终止以便分词 */
|
||||
memcpy(kbuf, buf, count);
|
||||
kbuf[count] = '\0';
|
||||
|
||||
p = kbuf;
|
||||
|
||||
/* 以空白字符或逗号分隔(支持 "100 4"、"100,4"、"0x64 0x4" 等格式) */
|
||||
token = strsep(&p, " \t\n,");
|
||||
if (!token || *token == '\0') {
|
||||
dev_err(dev, "%s: missing threshold parameter\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = kstrtoul(token, 0, &val); /* base=0 支持 0x 前缀 */
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: invalid threshold '%s'\n", __func__, token);
|
||||
return ret;
|
||||
}
|
||||
g_init_threshold = (unsigned int)val;
|
||||
|
||||
/* 第二个参数可选:lanes */
|
||||
token = strsep(&p, " \t\n,");
|
||||
if (token && *token != '\0') {
|
||||
ret = kstrtoul(token, 0, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: invalid lanes '%s'\n", __func__, token);
|
||||
return ret;
|
||||
}
|
||||
g_freqs = (unsigned int)val;
|
||||
link->rate = g_freqs;
|
||||
} else {
|
||||
/* 如果未提供 lanes,则保持原值(或你可以改为返回错误以强制要求两个参数) */
|
||||
dev_info(dev, "%s: freq not provided, keep %u\n", __func__, link->rate);
|
||||
}
|
||||
|
||||
|
||||
dev_err(dev, "%s, %d, g_init_threshold = %lu, g_freq = %u\n",
|
||||
__func__, __LINE__, g_init_threshold, link->rate);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_WO(dpinit);
|
||||
|
||||
int dw_dp_create_capabilities_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
device_create_file(&pdev->dev, &dev_attr_dpinit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dw_dp_remove_capabilities_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
device_remove_file(&pdev->dev, &dev_attr_dpinit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_dp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_dp *dp;
|
||||
void __iomem *base;
|
||||
int id, ret;
|
||||
|
||||
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
@@ -4451,7 +4409,7 @@ static int dw_dp_probe(struct platform_device *pdev)
|
||||
ret = dw_dp_parse_dt(dp);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to parse DT\n");
|
||||
dp->force_hpd = 1;
|
||||
|
||||
mutex_init(&dp->irq_lock);
|
||||
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
|
||||
init_completion(&dp->complete);
|
||||
@@ -4465,12 +4423,10 @@ static int dw_dp_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, PTR_ERR(dp->regmap),
|
||||
"failed to create regmap\n");
|
||||
|
||||
#if !NO_PHY
|
||||
dp->phy = devm_of_phy_get(dev, dev->of_node, NULL);
|
||||
if (IS_ERR(dp->phy))
|
||||
return dev_err_probe(dev, PTR_ERR(dp->phy),
|
||||
"failed to get phy\n");
|
||||
#endif
|
||||
dp->i2s_clk = devm_clk_get(dev, "i2s");
|
||||
if (IS_ERR(dp->i2s_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dp->i2s_clk),
|
||||
@@ -4554,6 +4510,28 @@ static int dw_dp_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register extcon device\n");
|
||||
|
||||
/* Try to get PHY's extcon for Type-C HPD notification */
|
||||
dp->phy_extcon = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(dp->phy_extcon)) {
|
||||
if (PTR_ERR(dp->phy_extcon) == -EPROBE_DEFER) {
|
||||
dev_err(dev, "PHY extcon not ready, deferring probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
/* PHY extcon not available, will use GPIO or internal HPD */
|
||||
dev_info(dev, "No PHY extcon found, using GPIO/internal HPD detection\n");
|
||||
dp->phy_extcon = NULL;
|
||||
} else {
|
||||
/* Register notifier to listen for PHY HPD events */
|
||||
dp->phy_extcon_nb.notifier_call = dw_dp_phy_extcon_notifier;
|
||||
ret = devm_extcon_register_notifier(dev, dp->phy_extcon,
|
||||
EXTCON_DISP_DP,
|
||||
&dp->phy_extcon_nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register PHY extcon notifier: ret = %d \n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//ret = dw_dp_register_audio_driver(dp);
|
||||
//if (ret)
|
||||
// return ret;
|
||||
@@ -4592,7 +4570,6 @@ static int dw_dp_probe(struct platform_device *pdev)
|
||||
secondary->split_mode = true;
|
||||
}
|
||||
|
||||
dw_dp_create_capabilities_sysfs(pdev);
|
||||
//dw_dp_hdcp_init(dp);
|
||||
|
||||
return component_add(dev, &dw_dp_component_ops);
|
||||
@@ -4604,7 +4581,6 @@ static int dw_dp_remove(struct platform_device *pdev)
|
||||
|
||||
component_del(dp->dev, &dw_dp_component_ops);
|
||||
cancel_work_sync(&dp->hpd_work);
|
||||
dw_dp_remove_capabilities_sysfs(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,22 +26,26 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kobject.h>
|
||||
|
||||
/* PHY Lane Mux Selection */
|
||||
#define PHY_LANE_MUX_USB 0
|
||||
#define PHY_LANE_MUX_DP 1
|
||||
|
||||
/* Base DWC3 Control Register */
|
||||
#define DWC3_LCSR_TX_DEEMPH_2 0xd068
|
||||
|
||||
/*
|
||||
* DPTX_SYS Register Offsets - From soc sys databook
|
||||
*/
|
||||
#define DPTX_CTRL 0x00
|
||||
#define DPTX_LSFR_CTRL 0x10
|
||||
#define DPTX_LSFR_SEED 0x14
|
||||
#define DPTX_MTN_LINK 0x20
|
||||
#define DPTX_AUX_CTRL 0x40
|
||||
#define DPTX_DBG0 0x100
|
||||
#define DPTX_DBG1 0x104
|
||||
#define DPTX_CTRL 0x00
|
||||
#define DPTX_LSFR_CTRL 0x10
|
||||
#define DPTX_LSFR_SEED 0x14
|
||||
#define DPTX_MTN_LINK 0x20
|
||||
#define DPTX_AUX_CTRL 0x40
|
||||
#define DPTX_DBG0 0x100
|
||||
#define DPTX_DBG1 0x104
|
||||
|
||||
/* DPTX_AUX_CTRL bits*/
|
||||
#define AUX_DP_DN_SWAP BIT(8)
|
||||
#define AUX_DP_DN_SWAP BIT(8)
|
||||
|
||||
/*
|
||||
* TCA Register Offsets - From Databook Section 3.2 (Page 36)
|
||||
@@ -53,7 +57,10 @@
|
||||
#define TCA_CLK_RST_SUSPEND_CLK_EN BIT(0)
|
||||
|
||||
#define TCA_INTR_EN 0x04
|
||||
#define TCA_INTR_EN_MASK GENMASK(1, 0)
|
||||
|
||||
#define TCA_INTR_STS 0x08
|
||||
#define TCA_INTR_STS_MASK GENMASK(15, 0)
|
||||
|
||||
#define TCA_GCFG 0x10
|
||||
#define TCA_GCFG_ROLE_HSTDEV BIT(4)
|
||||
@@ -68,13 +75,18 @@
|
||||
#define TCA_TCPC_MUX_CONTRL GENMASK(1, 0)
|
||||
#define TCA_TCPC_MUX_CONTRL_NO_CONN 0
|
||||
#define TCA_TCPC_MUX_CONTRL_USB_CONN 1
|
||||
#define TCA_TCPC_MUX_CONTRL_DP_CONN 2
|
||||
#define TCA_TCPC_MUX_CONTRL_USBDP_CONN 3
|
||||
|
||||
#define TCA_SYSMODE_CFG 0x18
|
||||
#define TCA_SYSMODE_TCPC_DISABLE BIT(3)
|
||||
#define TCA_SYSMODE_TCPC_FLIP BIT(2)
|
||||
#define TCA_SYSMODE_TCPC_CONN_MODE GENMASK(1, 0)
|
||||
#define TCA_SYSMODE_TCPC_CONN_CE 0x02
|
||||
#define TCA_SYSMODE_TCPC_CONN_DF 0x03
|
||||
|
||||
#define TCA_CTRLSYNCMODE_CFG0 0x20
|
||||
#define TCA_CTRLSYNCMODE_CFG1 0x20
|
||||
#define TCA_CTRLSYNCMODE_CFG1 0x20
|
||||
|
||||
#define TCA_PSTATE 0x30
|
||||
#define TCA_PSTATE_CM_STS BIT(4)
|
||||
@@ -88,6 +100,7 @@
|
||||
#define TCA_GEN_TYPEC_FLIP_INVERT BIT(4)
|
||||
#define TCA_GEN_PHY_TYPEC_DISABLE BIT(3)
|
||||
#define TCA_GEN_PHY_TYPEC_FLIP BIT(2)
|
||||
#define TCA_GEN_PHY_TYPEC_CONN GENMASK(1, 0)
|
||||
|
||||
#define TCA_VBUS_CTRL 0x40
|
||||
#define TCA_VBUS_STATUS 0x44
|
||||
@@ -100,43 +113,99 @@
|
||||
#define PHY_MODE_DP_CUSTOM BIT(1)
|
||||
#define PHY_MODE_USB3_DP (BIT(0) | BIT(1))
|
||||
|
||||
enum {
|
||||
UDPHY_MODE_NONE = 0,
|
||||
UDPHY_MODE_USB = BIT(0),
|
||||
UDPHY_MODE_DP = BIT(1),
|
||||
UDPHY_MODE_DP_USB = BIT(1) | BIT(0),
|
||||
};
|
||||
|
||||
static char *udphy_mode_str[] = {
|
||||
"UDPHY_MODE_NONE",
|
||||
"UDPHY_MODE_USB",
|
||||
"UDPHY_MODE_DP",
|
||||
"UDPHY_MODE_DP_USB",
|
||||
};
|
||||
|
||||
struct zhihe_c10phy_priv {
|
||||
struct device *dev;
|
||||
struct phy *phy;
|
||||
struct phy *phy;
|
||||
struct phy *dp_phy;
|
||||
void __iomem *phy_ctrl_base;
|
||||
void __iomem *tca_base;
|
||||
void __iomem *sysreg_base;
|
||||
void __iomem *dptxsys_base;
|
||||
struct reset_control *c10phy_rst;
|
||||
enum typec_orientation orientation; /* Type-C orientation */
|
||||
enum typec_orientation orientation; /* Type-C orientation */
|
||||
u32 lane_mux_sel[4];
|
||||
u32 dp_lane_sel[4];
|
||||
/* GPIO for AUX control */
|
||||
struct gpio_desc *aux_p_gpio; /* AUX_P pull-down control */
|
||||
struct gpio_desc *aux_n_gpio; /* AUX_N pull-up control */
|
||||
enum usb_role usb_role; /* Current USB role: host or device */
|
||||
enum usb_role usb_role; /* Current USB role: host or device */
|
||||
struct typec_mux_dev *mux;
|
||||
u8 mode;
|
||||
bool mode_change;
|
||||
struct typec_switch_dev *sw;
|
||||
struct mutex lock;
|
||||
struct extcon_dev *extcon; /* Extcon device for HPD notification */
|
||||
};
|
||||
|
||||
/* Supported extcon cables - must be static to persist after probe returns */
|
||||
static const unsigned int c10phy_extcon_cables[] = {
|
||||
EXTCON_DISP_DP,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static void tca_blk_orientation_set(struct zhihe_c10phy_priv *priv,
|
||||
enum typec_orientation orientation);
|
||||
|
||||
static int udphy_set_mux(struct zhihe_c10phy_priv *priv);
|
||||
|
||||
static int tca_blk_typec_switch_set(struct typec_switch_dev *sw,
|
||||
enum typec_orientation orientation)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = typec_switch_get_drvdata(sw);
|
||||
|
||||
if (priv->orientation == orientation)
|
||||
return 0;
|
||||
|
||||
tca_blk_orientation_set(priv, orientation);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void configure_aux_gpio(struct zhihe_c10phy_priv *priv, enum typec_orientation orientation)
|
||||
{
|
||||
if (orientation == TYPEC_ORIENTATION_REVERSE) {
|
||||
/* Flipped orientation: swap the polarity */
|
||||
if (priv->aux_p_gpio) {
|
||||
gpiod_set_value_cansleep(priv->aux_p_gpio, 1);
|
||||
dev_dbg(priv->dev, "AUX_P GPIO set to high (pull-up) - flipped\n");
|
||||
}
|
||||
|
||||
if (priv->aux_n_gpio) {
|
||||
gpiod_set_value_cansleep(priv->aux_n_gpio, 0);
|
||||
dev_dbg(priv->dev, "AUX_N GPIO set to low (pull-down) - flipped\n");
|
||||
}
|
||||
} else {
|
||||
/* Normal orientation */
|
||||
if (priv->aux_p_gpio) {
|
||||
gpiod_set_value_cansleep(priv->aux_p_gpio, 0);
|
||||
dev_dbg(priv->dev, "AUX_P GPIO set to low (pull-down) - normal\n");
|
||||
}
|
||||
|
||||
if (priv->aux_n_gpio) {
|
||||
gpiod_set_value_cansleep(priv->aux_n_gpio, 1);
|
||||
dev_dbg(priv->dev, "AUX_N GPIO set to high (pull-up) - normal\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tca_blk_orientation_set(struct zhihe_c10phy_priv *priv,
|
||||
enum typec_orientation orientation)
|
||||
{
|
||||
u32 val;
|
||||
u32 val, dptx_aux_ctrl;
|
||||
|
||||
if (priv->orientation == orientation)
|
||||
return;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
@@ -150,10 +219,11 @@ static void tca_blk_orientation_set(struct zhihe_c10phy_priv *priv,
|
||||
|
||||
val = TCA_TCPC_VALID | TCA_TCPC_LOW_POWER_EN;
|
||||
writel(val, priv->tca_base + TCA_TCPC);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
dptx_aux_ctrl = readl(priv->dptxsys_base + DPTX_AUX_CTRL);
|
||||
|
||||
/* use System Configuration Mode for TCA mux control. */
|
||||
val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYSMODE);
|
||||
writel(val, priv->tca_base + TCA_GCFG);
|
||||
@@ -163,12 +233,18 @@ static void tca_blk_orientation_set(struct zhihe_c10phy_priv *priv,
|
||||
val |= TCA_SYSMODE_TCPC_DISABLE;
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
|
||||
if (orientation == TYPEC_ORIENTATION_REVERSE)
|
||||
if (orientation == TYPEC_ORIENTATION_REVERSE) {
|
||||
val |= TCA_SYSMODE_TCPC_FLIP;
|
||||
else if (orientation == TYPEC_ORIENTATION_NORMAL)
|
||||
dptx_aux_ctrl |= AUX_DP_DN_SWAP;
|
||||
} else if (orientation == TYPEC_ORIENTATION_NORMAL) {
|
||||
val &= ~TCA_SYSMODE_TCPC_FLIP;
|
||||
dptx_aux_ctrl &= ~AUX_DP_DN_SWAP;
|
||||
}
|
||||
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
writel(dptx_aux_ctrl, priv->dptxsys_base + DPTX_AUX_CTRL);
|
||||
|
||||
configure_aux_gpio(priv, orientation);
|
||||
|
||||
/* Enable TCA module */
|
||||
val &= ~TCA_SYSMODE_TCPC_DISABLE;
|
||||
@@ -219,20 +295,399 @@ static int zhihe_setup_typec_switch(struct zhihe_c10phy_priv *priv)
|
||||
static void zhihe_switch_unregister(void *data)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = data;
|
||||
|
||||
typec_switch_unregister(priv->sw);
|
||||
}
|
||||
|
||||
static int c10phy_init(struct phy *phy)
|
||||
{
|
||||
int ret = 0;
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
reset_control_deassert(priv->c10phy_rst);
|
||||
tca_blk_init(priv);
|
||||
ret = udphy_set_mux(priv);
|
||||
reset_control_assert(priv->c10phy_rst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
┌────────────────────┬───────────┬─────────┬──────────────────────────────┬────────────────┐
|
||||
│ Scenario │ HPD_STATE │ IRQ_HPD │ PHY Action │ DP Host Action │
|
||||
├────────────────────┼───────────┼─────────┼──────────────────────────────┼────────────────┤
|
||||
│ Display insertion │ 0→1 │ 0 │ extcon_set_state_sync(true) │ Long HPD │
|
||||
├────────────────────┼───────────┼─────────┼──────────────────────────────┼────────────────┤
|
||||
│ Display removal │ 1→0 │ 0 │ extcon_set_state_sync(false) │ Long HPD │
|
||||
├────────────────────┼───────────┼─────────┼──────────────────────────────┼────────────────┤
|
||||
│ Link status change │ 1 │ 1 │ extcon_sync() │ Short HPD │
|
||||
├────────────────────┼───────────┼─────────┼──────────────────────────────┼────────────────┤
|
||||
│ EDID update │ 1 │ 1 │ extcon_sync() │ Short HPD │
|
||||
├────────────────────┼───────────┼─────────┼──────────────────────────────┼────────────────┤
|
||||
│ Resolution switch │ 1 │ 1 │ extcon_sync() │ Short HPD │
|
||||
└────────────────────┴───────────┴─────────┴──────────────────────────────┴────────────────┘
|
||||
*/
|
||||
static int usbdp_typec_mux_set(struct typec_mux_dev *mux,
|
||||
struct typec_mux_state *state)
|
||||
{
|
||||
int ret;
|
||||
struct zhihe_c10phy_priv *priv = typec_mux_get_drvdata(mux);
|
||||
u8 mode;
|
||||
struct device *dev = priv->dev;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
switch (state->mode) {
|
||||
case TYPEC_DP_STATE_C:
|
||||
fallthrough;
|
||||
case TYPEC_DP_STATE_E:
|
||||
mode = UDPHY_MODE_DP;
|
||||
break;
|
||||
case TYPEC_DP_STATE_D:
|
||||
fallthrough;
|
||||
default:
|
||||
mode = UDPHY_MODE_DP_USB;
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(dev, "change mux_mode %s to %s\n", udphy_mode_str[priv->mode], udphy_mode_str[mode]);
|
||||
|
||||
if (mode != priv->mode) {
|
||||
priv->mode = mode;
|
||||
ret = udphy_set_mux(priv);
|
||||
}
|
||||
|
||||
/* Handling DP Alt Mode HPD events */
|
||||
if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
|
||||
struct typec_displayport_data *data = state->data;
|
||||
bool last_hpd_state, current_hpd_state;
|
||||
|
||||
if (!data) {
|
||||
/* No data means DP disconnected */
|
||||
dev_info(dev, "DP disconnected (no data)\n");
|
||||
last_hpd_state = extcon_get_state(priv->extcon, EXTCON_DISP_DP);
|
||||
if (!last_hpd_state)
|
||||
extcon_sync(priv->extcon, EXTCON_DISP_DP);
|
||||
else
|
||||
extcon_set_state_sync(priv->extcon, EXTCON_DISP_DP, false);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_info(dev, "DP Status: 0x%x (HPD_STATE=%d, IRQ_HPD=%d)\n",
|
||||
data->status,
|
||||
!!(data->status & DP_STATUS_HPD_STATE),
|
||||
!!(data->status & DP_STATUS_IRQ_HPD));
|
||||
|
||||
/* Get the last HPD status */
|
||||
last_hpd_state = extcon_get_state(priv->extcon, EXTCON_DISP_DP);
|
||||
|
||||
/* Get the current HPD status*/
|
||||
current_hpd_state = !!(data->status & DP_STATUS_HPD_STATE);
|
||||
|
||||
/* Prioritize processing IRQ_HPD (short pulse) */
|
||||
if (data->status & DP_STATUS_IRQ_HPD) {
|
||||
if (current_hpd_state == last_hpd_state && current_hpd_state) {
|
||||
/*
|
||||
* A normal short HPD signal: HPD remains high + IRQ pulse.
|
||||
* This is used to notify of link status changes, EDID updates, etc.
|
||||
*/
|
||||
dev_info(dev, "DP IRQ_HPD: Short pulse (Link status/EDID change)\n");
|
||||
extcon_sync(priv->extcon, EXTCON_DISP_DP);
|
||||
} else {
|
||||
/*
|
||||
* Abnormal condition: HPD low level + IRQ pulse
|
||||
* It could be a Partner firmware bug or a transitional state during the unplugging process.
|
||||
*/
|
||||
dev_warn(dev, "Abnormal: IRQ_HPD with current_hpd_state %d, last_hpd_state %d\n", current_hpd_state, last_hpd_state);
|
||||
/*
|
||||
* In this situation, we cannot determine whether it is a genuine disconnection or just jitter,
|
||||
* because in a Type-C scenario, we cannot reread the hardware state;
|
||||
* we can only wait for the Partner to send the next Attention VDM.
|
||||
*/
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Handling HPD status changes (long HPD: insertion/removal) */
|
||||
if (current_hpd_state != last_hpd_state) {
|
||||
dev_info(dev, "DP HPD state change: %s -> %s\n",
|
||||
last_hpd_state ? "connected" : "disconnected",
|
||||
current_hpd_state ? "connected" : "disconnected");
|
||||
|
||||
/* Notify DP Host of HPD status change*/
|
||||
extcon_set_state_sync(priv->extcon, EXTCON_DISP_DP, current_hpd_state);
|
||||
} else {
|
||||
/*
|
||||
* The state has not changed and there is no IRQ_HPD.
|
||||
* This could be a duplicate Attention VDM, ignore it.
|
||||
*/
|
||||
dev_dbg(dev, "DP HPD state unchanged (%s), ignoring\n", current_hpd_state ? "connected" : "disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int udphy_setup_typec_mux(struct zhihe_c10phy_priv *priv)
|
||||
{
|
||||
struct typec_mux_desc mux_desc = {};
|
||||
|
||||
mux_desc.drvdata = priv;
|
||||
mux_desc.fwnode = dev_fwnode(priv->dev);
|
||||
mux_desc.set = usbdp_typec_mux_set;
|
||||
|
||||
priv->mux = typec_mux_register(priv->dev, &mux_desc);
|
||||
if (IS_ERR(priv->mux)) {
|
||||
dev_err(priv->dev, "Error register typec mux: %ld\n",
|
||||
PTR_ERR(priv->mux));
|
||||
return PTR_ERR(priv->mux);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void udphy_typec_mux_unregister(void *data)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = data;
|
||||
|
||||
typec_mux_unregister(priv->mux);
|
||||
}
|
||||
|
||||
static int zhihe_dp_phy_verify_link_rate(unsigned int link_rate)
|
||||
{
|
||||
switch (link_rate) {
|
||||
case 1620:
|
||||
case 2700:
|
||||
case 5400:
|
||||
case 8100:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 udphy_dp_get_max_link_rate(struct zhihe_c10phy_priv *priv, struct device_node *np)
|
||||
{
|
||||
u32 max_link_rate;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "max-link-rate", &max_link_rate);
|
||||
if (ret)
|
||||
return 8100;
|
||||
|
||||
ret = zhihe_dp_phy_verify_link_rate(max_link_rate);
|
||||
if (ret) {
|
||||
dev_warn(priv->dev, "invalid max-link-rate value:%d\n", max_link_rate);
|
||||
max_link_rate = 8100;
|
||||
}
|
||||
|
||||
return max_link_rate;
|
||||
}
|
||||
|
||||
static int zhihe_dp_phy_verify_config(struct zhihe_c10phy_priv *priv,
|
||||
struct phy_configure_opts_dp *dp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* If changing link rate was required, verify it's supported. */
|
||||
ret = zhihe_dp_phy_verify_link_rate(dp->link_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Verify lane count. */
|
||||
switch (dp->lanes) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
/* valid lane count. */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zhihe_dp_phy_configure(struct phy *phy,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = zhihe_dp_phy_verify_config(priv, &opts->dp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udphy_dplane_get(struct zhihe_c10phy_priv *priv)
|
||||
{
|
||||
int dp_lanes;
|
||||
|
||||
switch (priv->mode) {
|
||||
case UDPHY_MODE_DP:
|
||||
dp_lanes = 4;
|
||||
break;
|
||||
case UDPHY_MODE_DP_USB:
|
||||
dp_lanes = 2;
|
||||
break;
|
||||
case UDPHY_MODE_USB:
|
||||
fallthrough;
|
||||
default:
|
||||
dp_lanes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return dp_lanes;
|
||||
}
|
||||
|
||||
static int udphy_sys_cfg(struct zhihe_c10phy_priv *priv)
|
||||
{
|
||||
u32 val, gen_status;
|
||||
|
||||
/* use System Configuration Mode for TCA mux control. */
|
||||
val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYSMODE);
|
||||
writel(val, priv->tca_base + TCA_GCFG);
|
||||
|
||||
/* read xbar cfg and write xbar val to sysmode reg*/
|
||||
gen_status = readl(priv->tca_base + TCA_GEN_STATUS);
|
||||
val = readl(priv->tca_base + TCA_SYSMODE_CFG);
|
||||
val &= ~(TCA_SYSMODE_TCPC_DISABLE | TCA_SYSMODE_TCPC_FLIP | TCA_SYSMODE_TCPC_CONN_MODE);
|
||||
gen_status &= TCA_SYSMODE_TCPC_DISABLE | TCA_SYSMODE_TCPC_FLIP | TCA_SYSMODE_TCPC_CONN_MODE;
|
||||
val |= gen_status;
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
|
||||
/* set typec disable */
|
||||
val = readl(priv->tca_base + TCA_SYSMODE_CFG);
|
||||
val |= TCA_SYSMODE_TCPC_DISABLE;
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
usleep_range(10, 20);
|
||||
|
||||
val = readl(priv->tca_base + TCA_SYSMODE_CFG);
|
||||
if (priv->orientation == TYPEC_ORIENTATION_REVERSE)
|
||||
val |= TCA_SYSMODE_TCPC_FLIP;
|
||||
else
|
||||
val &= ~TCA_SYSMODE_TCPC_FLIP;
|
||||
|
||||
/* set conn mode */
|
||||
val &= ~TCA_SYSMODE_TCPC_CONN_MODE;
|
||||
switch (priv->mode) {
|
||||
case UDPHY_MODE_DP:
|
||||
val |= TCA_SYSMODE_TCPC_CONN_CE;
|
||||
break;
|
||||
case UDPHY_MODE_DP_USB:
|
||||
case UDPHY_MODE_USB:
|
||||
default:
|
||||
val |= TCA_SYSMODE_TCPC_CONN_DF;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
usleep_range(10, 20);
|
||||
|
||||
/* set typec enable */
|
||||
val &= ~TCA_SYSMODE_TCPC_DISABLE;
|
||||
|
||||
writel(val, priv->tca_base + TCA_SYSMODE_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udphy_sync_cfg(struct zhihe_c10phy_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Use sync configuration mode for TCA mux control. */
|
||||
val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYNCMODE);
|
||||
writel(val, priv->tca_base + TCA_GCFG);
|
||||
|
||||
/*enable tca ack and timeout irq*/
|
||||
|
||||
writel(TCA_INTR_STS_MASK, priv->tca_base + TCA_INTR_STS);
|
||||
writel(TCA_INTR_EN_MASK, priv->tca_base + TCA_INTR_EN);
|
||||
|
||||
/*mode config */
|
||||
val = readl(priv->tca_base + TCA_TCPC);
|
||||
/*default set to usb mode*/
|
||||
val &= ~TCA_TCPC_MUX_CONTRL;
|
||||
val |= TCA_TCPC_MUX_CONTRL_USB_CONN;
|
||||
val |= TCA_TCPC_VALID;
|
||||
val &= ~TCA_TCPC_LOW_POWER_EN;
|
||||
if (priv->orientation == TYPEC_ORIENTATION_REVERSE)
|
||||
val |= TCA_TCPC_ORIENTATION_NORMAL;
|
||||
else
|
||||
val &= ~TCA_TCPC_ORIENTATION_NORMAL;
|
||||
writel(val, priv->tca_base + TCA_TCPC);
|
||||
usleep_range(10, 20);
|
||||
|
||||
val = readl(priv->tca_base + TCA_TCPC);
|
||||
val &= ~TCA_TCPC_LOW_POWER_EN;
|
||||
val &= ~TCA_TCPC_MUX_CONTRL;
|
||||
switch (priv->mode) {
|
||||
case UDPHY_MODE_USB:
|
||||
return 0;
|
||||
case UDPHY_MODE_DP:
|
||||
val |= TCA_TCPC_MUX_CONTRL_DP_CONN;
|
||||
break;
|
||||
case UDPHY_MODE_DP_USB:
|
||||
default:
|
||||
val |= TCA_TCPC_MUX_CONTRL_USBDP_CONN;
|
||||
break;
|
||||
}
|
||||
|
||||
val |= TCA_TCPC_VALID;
|
||||
writel(val, priv->tca_base + TCA_TCPC);
|
||||
usleep_range(10, 20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udphy_set_mux(struct zhihe_c10phy_priv *priv)
|
||||
{
|
||||
udphy_sys_cfg(priv);
|
||||
usleep_range(10, 20);
|
||||
udphy_sync_cfg(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zhihe_dp_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0, dp_lanes;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
dp_lanes = udphy_dplane_get(priv);
|
||||
phy_set_bus_width(phy, dp_lanes);
|
||||
mutex_unlock(&priv->lock);
|
||||
/*
|
||||
* If data send by aux channel too fast after phy power on,
|
||||
* the aux may be not ready which will cause aux error. Adding
|
||||
* delay to avoid this issue.
|
||||
*/
|
||||
usleep_range(10000, 11000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zhihe_dp_phy_power_off(struct phy *phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops zhihe_dp_phy_ops = {
|
||||
.power_on = zhihe_dp_phy_power_on,
|
||||
.power_off = zhihe_dp_phy_power_off,
|
||||
.configure = zhihe_dp_phy_configure,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int c10phy_exit(struct phy *phy)
|
||||
{
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
@@ -254,8 +709,7 @@ static const struct phy_ops c10phy_ops = {
|
||||
static ssize_t orientation_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct phy *phy = dev_get_drvdata(dev);
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
struct zhihe_c10phy_priv *priv = dev_get_drvdata(dev);
|
||||
const char *orientation_str;
|
||||
|
||||
switch (priv->orientation) {
|
||||
@@ -280,8 +734,7 @@ static ssize_t orientation_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct phy *phy = dev_get_drvdata(dev);
|
||||
struct zhihe_c10phy_priv *priv = phy_get_drvdata(phy);
|
||||
struct zhihe_c10phy_priv *priv = dev_get_drvdata(dev);
|
||||
enum typec_orientation orientation;
|
||||
|
||||
if (sysfs_streq(buf, "none"))
|
||||
@@ -309,21 +762,65 @@ static const struct attribute_group c10phy_attr_group = {
|
||||
.attrs = c10phy_attrs,
|
||||
};
|
||||
|
||||
static int c10phy_probe(struct platform_device *pdev)
|
||||
static int udphy_parse_lane_mux_data(struct zhihe_c10phy_priv *priv, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct property *prop;
|
||||
int ret, i, len, num_lanes;
|
||||
|
||||
prop = of_find_property(np, "zhihe,dp-lane-mux", &len);
|
||||
if (!prop) {
|
||||
dev_dbg(dev, "failed to find dp lane mux, following dp alt mode\n");
|
||||
priv->mode = UDPHY_MODE_USB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_lanes = len / sizeof(u32);
|
||||
|
||||
if (num_lanes != 2 && num_lanes != 4) {
|
||||
dev_err(dev, "invalid number of lane mux\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, "zhihe,dp-lane-mux", priv->dp_lane_sel, num_lanes);
|
||||
if (ret) {
|
||||
dev_err(dev, "get dp lane mux failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_lanes; i++) {
|
||||
int j;
|
||||
|
||||
if (priv->dp_lane_sel[i] > 3) {
|
||||
dev_err(dev, "lane mux between 0 and 3, exceeding the range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->lane_mux_sel[priv->dp_lane_sel[i]] = PHY_LANE_MUX_DP;
|
||||
|
||||
for (j = i + 1; j < num_lanes; j++) {
|
||||
if (priv->dp_lane_sel[i] == priv->dp_lane_sel[j]) {
|
||||
dev_err(dev, "set repeat lane mux value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
priv->mode = UDPHY_MODE_DP;
|
||||
if (num_lanes == 2) {
|
||||
priv->mode |= UDPHY_MODE_USB;
|
||||
if (priv->lane_mux_sel[0] == PHY_LANE_MUX_DP)
|
||||
priv->orientation = TYPEC_ORIENTATION_REVERSE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int c10phy_parse_dt(struct zhihe_c10phy_priv *priv, struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct zhihe_c10phy_priv *priv;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->usb_role = USB_ROLE_HOST; /* Default to host mode, can be changed dynamically */
|
||||
/* Get TCA register base */
|
||||
priv->tca_base = devm_platform_ioremap_resource_byname(pdev, "tca");
|
||||
if (IS_ERR(priv->tca_base))
|
||||
@@ -370,12 +867,41 @@ static int c10phy_probe(struct platform_device *pdev)
|
||||
if (priv->aux_n_gpio)
|
||||
dev_info(dev, "AUX_N GPIO configured for pull-up control\n");
|
||||
|
||||
ret = udphy_parse_lane_mux_data(priv, dev);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to get lane mux\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int c10phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
struct zhihe_c10phy_priv *priv;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* Default to host mode, can be changed dynamically */
|
||||
priv->usb_role = USB_ROLE_HOST;
|
||||
|
||||
ret = c10phy_parse_dt(priv, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup Type-C support if enabled */
|
||||
if (device_property_present(dev, "orientation-switch")) {
|
||||
ret = zhihe_setup_typec_switch(priv);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, zhihe_switch_unregister, priv);
|
||||
if (ret)
|
||||
@@ -386,14 +912,54 @@ static int c10phy_probe(struct platform_device *pdev)
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
/* Create PHY */
|
||||
priv->phy = devm_phy_create(dev, NULL, &c10phy_ops);
|
||||
if (IS_ERR(priv->phy))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->phy),
|
||||
"Couldn't create phy\n");
|
||||
/* Initialize extcon device for HPD notification to DP driver */
|
||||
priv->extcon = devm_extcon_dev_allocate(dev, c10phy_extcon_cables);
|
||||
|
||||
dev_set_drvdata(dev, priv->phy);
|
||||
phy_set_drvdata(priv->phy, priv);
|
||||
if (IS_ERR(priv->extcon)) {
|
||||
dev_err(dev, "Failed to allocate extcon device: %ld\n", PTR_ERR(priv->extcon));
|
||||
return PTR_ERR(priv->extcon);
|
||||
}
|
||||
|
||||
ret = devm_extcon_dev_register(dev, priv->extcon);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register extcon device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "Extcon device registered for HPD notification, %s\n", extcon_get_edev_name(priv->extcon));
|
||||
|
||||
if (device_property_present(dev, "svid")) {
|
||||
ret = udphy_setup_typec_mux(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, udphy_typec_mux_unregister, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
if (of_node_name_eq(child_np, "dp-port")) {
|
||||
priv->dp_phy = devm_phy_create(dev, child_np, &zhihe_dp_phy_ops);
|
||||
if (IS_ERR(priv->dp_phy)) {
|
||||
dev_err(dev, "failed to create dp phy: %pOFn\n", child_np);
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
phy_set_bus_width(priv->dp_phy, udphy_dplane_get(priv));
|
||||
priv->dp_phy->attrs.max_link_rate = udphy_dp_get_max_link_rate(priv, child_np);
|
||||
phy_set_drvdata(priv->dp_phy, priv);
|
||||
} else if (of_node_name_eq(child_np, "u3-port")) {
|
||||
priv->phy = devm_phy_create(dev, child_np, &c10phy_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(dev, "failed to create usb phy: %pOFn\n", child_np);
|
||||
goto put_child;
|
||||
}
|
||||
phy_set_drvdata(priv->phy, priv);
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
/* Register PHY provider */
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
@@ -409,6 +975,10 @@ static int c10phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
put_child:
|
||||
of_node_put(child_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int c10phy_remove(struct platform_device *pdev)
|
||||
|
||||
Reference in New Issue
Block a user