* Copyright(c) 2020, Analogix Semiconductor. All rights reserved.
*
*/
+#include <linux/cleanup.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/role.h>
#include <linux/workqueue.h>
#include <linux/of_graph.h>
static void anx7625_disable_pd_protocol(struct anx7625_data *ctx)
{
struct device *dev = ctx->dev;
- int ret, val;
+ int ret;
/* Reset main ocm */
ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, 0x88, 0x40);
DRM_DEV_DEBUG_DRIVER(dev, "disable PD feature fail.\n");
else
DRM_DEV_DEBUG_DRIVER(dev, "disable PD feature succeeded.\n");
+}
+
+static void anx7625_configure_hpd(struct anx7625_data *ctx)
+{
+ int val;
/*
* Make sure the HPD GPIO already be configured after OCM release before
if ((ret & FLASH_LOAD_STA_CHK) != FLASH_LOAD_STA_CHK)
return -ENODEV;
- anx7625_disable_pd_protocol(ctx);
+ if (!ctx->typec_port)
+ anx7625_disable_pd_protocol(ctx);
+ anx7625_configure_hpd(ctx);
DRM_DEV_DEBUG_DRIVER(dev, "Firmware ver %02x%02x,",
anx7625_reg_read(ctx,
DRM_DEV_DEBUG_DRIVER(dev, "Secure OCM version=%02x\n", ret);
}
+#if IS_REACHABLE(CONFIG_TYPEC)
+static void anx7625_typec_set_orientation(struct anx7625_data *ctx)
+{
+ u32 val = anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, SYSTEM_STSTUS);
+
+ if (val & (CC1_RP | CC1_RD))
+ typec_set_orientation(ctx->typec_port, TYPEC_ORIENTATION_NORMAL);
+ else if (val & (CC2_RP | CC2_RD))
+ typec_set_orientation(ctx->typec_port, TYPEC_ORIENTATION_REVERSE);
+ else
+ typec_set_orientation(ctx->typec_port, TYPEC_ORIENTATION_NONE);
+}
+
+static void anx7625_typec_set_status(struct anx7625_data *ctx,
+ unsigned int intr_status,
+ unsigned int intr_vector)
+{
+ if (intr_vector & CC_STATUS)
+ anx7625_typec_set_orientation(ctx);
+ if (intr_vector & DATA_ROLE_STATUS) {
+ enum typec_data_role data_role = (intr_status & DATA_ROLE_STATUS) ?
+ TYPEC_HOST : TYPEC_DEVICE;
+ usb_role_switch_set_role(ctx->role_sw,
+ (intr_status & DATA_ROLE_STATUS) ?
+ USB_ROLE_HOST : USB_ROLE_DEVICE);
+ typec_set_data_role(ctx->typec_port, data_role);
+ ctx->typec_data_role = data_role;
+ }
+ if (intr_vector & VBUS_STATUS)
+ typec_set_pwr_role(ctx->typec_port,
+ (intr_status & VBUS_STATUS) ?
+ TYPEC_SOURCE : TYPEC_SINK);
+ if (intr_vector & VCONN_STATUS)
+ typec_set_vconn_role(ctx->typec_port,
+ (intr_status & VCONN_STATUS) ?
+ TYPEC_SOURCE : TYPEC_SINK);
+}
+
+static int anx7625_typec_register(struct anx7625_data *ctx)
+{
+ struct typec_capability typec_cap = { };
+ struct fwnode_handle *fwnode __free(fwnode_handle) =
+ device_get_named_child_node(ctx->dev, "connector");
+ u32 val;
+ int ret;
+
+ if (!fwnode)
+ return 0;
+
+ ret = typec_get_fw_cap(&typec_cap, fwnode);
+ if (ret < 0)
+ return ret;
+
+ typec_cap.revision = 0x0120;
+ typec_cap.pd_revision = 0x0300;
+ typec_cap.usb_capability = USB_CAPABILITY_USB2 | USB_CAPABILITY_USB3;
+ typec_cap.orientation_aware = true;
+
+ typec_cap.driver_data = ctx;
+
+ ctx->typec_port = typec_register_port(ctx->dev, &typec_cap);
+ if (IS_ERR(ctx->typec_port))
+ return PTR_ERR(ctx->typec_port);
+
+ ctx->role_sw = fwnode_usb_role_switch_get(fwnode);
+ if (IS_ERR(ctx->role_sw)) {
+ typec_unregister_port(ctx->typec_port);
+ return PTR_ERR(ctx->role_sw);
+ }
+
+ val = anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, SYSTEM_STSTUS);
+
+ anx7625_typec_set_status(ctx, val,
+ CC_STATUS | DATA_ROLE_STATUS |
+ VBUS_STATUS | VCONN_STATUS);
+
+ return 0;
+}
+
+static void anx7625_typec_unregister(struct anx7625_data *ctx)
+{
+ usb_role_switch_put(ctx->role_sw);
+ typec_unregister_port(ctx->typec_port);
+}
+#else
+static void anx7625_typec_set_status(struct anx7625_data *ctx,
+ unsigned int intr_status,
+ unsigned int intr_vector)
+{
+}
+
+static int anx7625_typec_register(struct anx7625_data *ctx)
+{
+ return 0;
+}
+
+static void anx7625_typec_unregister(struct anx7625_data *ctx)
+{
+}
+#endif
+
static int anx7625_read_hpd_status_p0(struct anx7625_data *ctx)
{
return anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, SYSTEM_STSTUS);
}
}
-static int anx7625_hpd_change_detect(struct anx7625_data *ctx)
+static int anx7625_intr_status(struct anx7625_data *ctx)
{
int intr_vector, status;
struct device *dev = ctx->dev;
return status;
}
- if (!(intr_vector & HPD_STATUS_CHANGE))
- return -ENOENT;
-
status = anx7625_reg_read(ctx, ctx->i2c.rx_p0_client,
SYSTEM_STSTUS);
if (status < 0) {
}
DRM_DEV_DEBUG_DRIVER(dev, "0x7e:0x45=%x\n", status);
+
+ anx7625_typec_set_status(ctx, status, intr_vector);
+
+ if (!(intr_vector & HPD_STATUS))
+ return -ENOENT;
+
dp_hpd_change_handler(ctx, status & HPD_STATUS);
return 0;
return;
}
- event = anx7625_hpd_change_detect(ctx);
+ event = anx7625_intr_status(ctx);
mutex_unlock(&ctx->lock);
}
if (!platform->pdata.low_power_mode) {
- anx7625_disable_pd_protocol(platform);
+ struct fwnode_handle *fwnode;
+
+ fwnode = device_get_named_child_node(dev, "connector");
+ if (fwnode)
+ fwnode_handle_put(fwnode);
+ else
+ anx7625_disable_pd_protocol(platform);
+
+ anx7625_configure_hpd(platform);
+
pm_runtime_get_sync(dev);
_anx7625_hpd_polling(platform, 5000 * 100);
}
+ if (platform->pdata.intp_irq)
+ anx7625_reg_write(platform, platform->i2c.rx_p0_client,
+ INTERFACE_CHANGE_INT_MASK, 0);
+
+ /* After getting runtime handle */
+ ret = anx7625_typec_register(platform);
+ if (ret)
+ goto pm_suspend;
+
/* Add work function */
if (platform->pdata.intp_irq) {
enable_irq(platform->pdata.intp_irq);
return 0;
+pm_suspend:
+ if (!platform->pdata.low_power_mode)
+ pm_runtime_put_sync_suspend(&client->dev);
+
free_wq:
if (platform->workqueue)
destroy_workqueue(platform->workqueue);
{
struct anx7625_data *platform = i2c_get_clientdata(client);
+ anx7625_typec_unregister(platform);
+
drm_bridge_remove(&platform->bridge);
if (platform->pdata.intp_irq)