diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 998b41ebc53e..d6abfdad803b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -121,6 +121,29 @@ struct cport_to_ep { __u8 endpoint_out; }; +/** + * timesync_enable_request - Enable timesync in an APBridge + * @count: number of TimeSync Pulses to expect + * @frame_time: the initial FrameTime at the first TimeSync Pulse + * @strobe_delay: the expected delay in microseconds between each TimeSync Pulse + * @refclk: The AP mandated reference clock to run FrameTime at + */ +struct timesync_enable_request { + __u8 count; + __le64 frame_time; + __le32 strobe_delay; + __le32 refclk; +} __packed; + +/** + * timesync_authoritative_request - Transmit authoritative FrameTime to APBridge + * @frame_time: An array of authoritative FrameTimes provided by the SVC + * and relayed to the APBridge by the AP + */ +struct timesync_authoritative_request { + __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; +} __packed; + static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; @@ -674,18 +697,126 @@ static int cport_features_disable(struct gb_host_device *hd, u16 cport_id) return retval; } +static int timesync_enable(struct gb_host_device *hd, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + struct gb_control_timesync_enable_request *request; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->count = count; + request->frame_time = cpu_to_le64(frame_time); + request->strobe_delay = cpu_to_le32(strobe_delay); + request->refclk = cpu_to_le32(refclk); + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_ENABLE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, request, + sizeof(*request), ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot enable timesync %d\n", retval); + + kfree(request); + return retval; +} + +static int timesync_disable(struct gb_host_device *hd) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_DISABLE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, NULL, + 0, ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot disable timesync %d\n", retval); + + return retval; +} + +static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time) +{ + int retval, i; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + struct timesync_authoritative_request *request; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) + request->frame_time[i] = cpu_to_le64(frame_time[i]); + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_AUTHORITATIVE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, request, + sizeof(*request), ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval); + + kfree(request); + return retval; +} + +static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + u64 *response_frame_time; + + response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL); + if (!response_frame_time) + return -ENOMEM; + + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + REQUEST_TIMESYNC_GET_LAST_EVENT, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, response_frame_time, + sizeof(*response_frame_time), ES2_TIMEOUT); + + if (retval != sizeof(*response_frame_time)) { + dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n", + retval); + + if (retval >= 0) + retval = -EIO; + + goto out; + } + *frame_time = le64_to_cpu(*response_frame_time); + retval = 0; +out: + kfree(response_frame_time); + return retval; +} + static struct gb_hd_driver es2_driver = { - .hd_priv_size = sizeof(struct es2_ap_dev), - .message_send = message_send, - .message_cancel = message_cancel, - .cport_allocate = es2_cport_allocate, - .cport_release = es2_cport_release, - .cport_enable = cport_enable, - .latency_tag_enable = latency_tag_enable, - .latency_tag_disable = latency_tag_disable, - .output = output, - .cport_features_enable = cport_features_enable, - .cport_features_disable = cport_features_disable, + .hd_priv_size = sizeof(struct es2_ap_dev), + .message_send = message_send, + .message_cancel = message_cancel, + .cport_allocate = es2_cport_allocate, + .cport_release = es2_cport_release, + .cport_enable = cport_enable, + .latency_tag_enable = latency_tag_enable, + .latency_tag_disable = latency_tag_disable, + .output = output, + .cport_features_enable = cport_features_enable, + .cport_features_disable = cport_features_disable, + .timesync_enable = timesync_enable, + .timesync_disable = timesync_disable, + .timesync_authoritative = timesync_authoritative, + .timesync_get_last_event = timesync_get_last_event, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e3ad5d70a9b6..d379fe36c7a5 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -188,30 +188,36 @@ struct gb_control_timesync_get_last_event_response { /* APBridge protocol */ /* request APB1 log */ -#define GB_APB_REQUEST_LOG 0x02 +#define GB_APB_REQUEST_LOG 0x02 /* request to map a cport to bulk in and bulk out endpoints */ -#define GB_APB_REQUEST_EP_MAPPING 0x03 +#define GB_APB_REQUEST_EP_MAPPING 0x03 /* request to get the number of cports available */ -#define GB_APB_REQUEST_CPORT_COUNT 0x04 +#define GB_APB_REQUEST_CPORT_COUNT 0x04 /* request to reset a cport state */ -#define GB_APB_REQUEST_RESET_CPORT 0x05 +#define GB_APB_REQUEST_RESET_CPORT 0x05 /* request to time the latency of messages on a given cport */ -#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 -#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 +#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 +#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 /* request to control the CSI transmitter */ -#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 +#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 /* request to control the CSI transmitter */ -#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 +#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 /* vendor requests to enable/disable CPort features */ -#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b -#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c +#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b +#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c + +/* TimeSync commands */ +#define REQUEST_TIMESYNC_ENABLE 0x0d +#define REQUEST_TIMESYNC_DISABLE 0x0e +#define REQUEST_TIMESYNC_AUTHORITATIVE 0x0f +#define REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 /* Firmware Download Protocol */ diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 80573aed56ef..8510816c1796 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -30,6 +30,13 @@ struct gb_hd_driver { bool async); int (*cport_features_enable)(struct gb_host_device *hd, u16 cport_id); int (*cport_features_disable)(struct gb_host_device *hd, u16 cport_id); + int (*timesync_enable)(struct gb_host_device *hd, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk); + int (*timesync_disable)(struct gb_host_device *hd); + int (*timesync_authoritative)(struct gb_host_device *hd, + u64 *frame_time); + int (*timesync_get_last_event)(struct gb_host_device *hd, + u64 *frame_time); }; struct gb_host_device {