summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/cdns3/cdnsp-debug.h305
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c3
-rw-r--r--drivers/usb/chipidea/udc.c2
-rw-r--r--drivers/usb/core/devio.c32
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hub.c8
-rw-r--r--drivers/usb/core/usb-acpi.c2
-rw-r--r--drivers/usb/dwc2/params.c10
-rw-r--r--drivers/usb/dwc3/core.c117
-rw-r--r--drivers/usb/dwc3/core.h13
-rw-r--r--drivers/usb/dwc3/drd.c13
-rw-r--r--drivers/usb/gadget/function/f_fs.c4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c70
-rw-r--r--drivers/usb/gadget/function/f_phonet.c4
-rw-r--r--drivers/usb/gadget/function/f_serial.c4
-rw-r--r--drivers/usb/gadget/function/f_uac1.c236
-rw-r--r--drivers/usb/gadget/function/f_uac2.c476
-rw-r--r--drivers/usb/gadget/function/u_audio.c238
-rw-r--r--drivers/usb/gadget/function/u_audio.h14
-rw-r--r--drivers/usb/gadget/function/u_uac1.h7
-rw-r--r--drivers/usb/gadget/function/u_uac2.h11
-rw-r--r--drivers/usb/gadget/function/uac_common.h9
-rw-r--r--drivers/usb/gadget/legacy/audio.c93
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c20
-rw-r--r--drivers/usb/host/ehci-pci.c9
-rw-r--r--drivers/usb/host/ehci-sched.c4
-rw-r--r--drivers/usb/host/fotg210-hcd.c4
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c7
-rw-r--r--drivers/usb/host/xhci-mtk.c37
-rw-r--r--drivers/usb/host/xhci-plat.c13
-rw-r--r--drivers/usb/misc/Kconfig10
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/qcom_eud.c251
-rw-r--r--drivers/usb/typec/Kconfig12
-rw-r--r--drivers/usb/typec/Makefile1
-rw-r--r--drivers/usb/typec/rt1719.c961
36 files changed, 2563 insertions, 440 deletions
diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h
index a8776df2d4e0..f0ca865cce2a 100644
--- a/drivers/usb/cdns3/cdnsp-debug.h
+++ b/drivers/usb/cdns3/cdnsp-debug.h
@@ -182,208 +182,211 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
int ep_id = TRB_TO_EP_INDEX(field3) - 1;
int type = TRB_FIELD_TO_TYPE(field3);
unsigned int ep_num;
- int ret = 0;
+ int ret;
u32 temp;
ep_num = DIV_ROUND_UP(ep_id, 2);
switch (type) {
case TRB_LINK:
- ret += snprintf(str, size,
- "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
- field1, field0, GET_INTR_TARGET(field2),
- cdnsp_trb_type_string(type),
- field3 & TRB_IOC ? 'I' : 'i',
- field3 & TRB_CHAIN ? 'C' : 'c',
- field3 & TRB_TC ? 'T' : 't',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
+ field1, field0, GET_INTR_TARGET(field2),
+ cdnsp_trb_type_string(type),
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_TC ? 'T' : 't',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_TRANSFER:
case TRB_COMPLETION:
case TRB_PORT_STATUS:
case TRB_HC_EVENT:
- ret += snprintf(str, size,
- "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
- " len %ld slot %ld flags %c:%c",
- ep_num, ep_id % 2 ? "out" : "in",
- TRB_TO_EP_INDEX(field3),
- cdnsp_trb_type_string(type), field1, field0,
- cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
- EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
- field3 & EVENT_DATA ? 'E' : 'e',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
+ " len %ld slot %ld flags %c:%c",
+ ep_num, ep_id % 2 ? "out" : "in",
+ TRB_TO_EP_INDEX(field3),
+ cdnsp_trb_type_string(type), field1, field0,
+ cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
+ EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
+ field3 & EVENT_DATA ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_MFINDEX_WRAP:
- ret += snprintf(str, size, "%s: flags %c",
- cdnsp_trb_type_string(type),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size, "%s: flags %c",
+ cdnsp_trb_type_string(type),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SETUP:
- ret += snprintf(str, size,
- "type '%s' bRequestType %02x bRequest %02x "
- "wValue %02x%02x wIndex %02x%02x wLength %d "
- "length %ld TD size %ld intr %ld Setup ID %ld "
- "flags %c:%c:%c",
- cdnsp_trb_type_string(type),
- field0 & 0xff,
- (field0 & 0xff00) >> 8,
- (field0 & 0xff000000) >> 24,
- (field0 & 0xff0000) >> 16,
- (field1 & 0xff00) >> 8,
- field1 & 0xff,
- (field1 & 0xff000000) >> 16 |
- (field1 & 0xff0000) >> 16,
- TRB_LEN(field2), GET_TD_SIZE(field2),
- GET_INTR_TARGET(field2),
- TRB_SETUPID_TO_TYPE(field3),
- field3 & TRB_IDT ? 'D' : 'd',
- field3 & TRB_IOC ? 'I' : 'i',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "type '%s' bRequestType %02x bRequest %02x "
+ "wValue %02x%02x wIndex %02x%02x wLength %d "
+ "length %ld TD size %ld intr %ld Setup ID %ld "
+ "flags %c:%c:%c",
+ cdnsp_trb_type_string(type),
+ field0 & 0xff,
+ (field0 & 0xff00) >> 8,
+ (field0 & 0xff000000) >> 24,
+ (field0 & 0xff0000) >> 16,
+ (field1 & 0xff00) >> 8,
+ field1 & 0xff,
+ (field1 & 0xff000000) >> 16 |
+ (field1 & 0xff0000) >> 16,
+ TRB_LEN(field2), GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ TRB_SETUPID_TO_TYPE(field3),
+ field3 & TRB_IDT ? 'D' : 'd',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DATA:
- ret += snprintf(str, size,
- "type '%s' Buffer %08x%08x length %ld TD size %ld "
- "intr %ld flags %c:%c:%c:%c:%c:%c:%c",
- cdnsp_trb_type_string(type),
- field1, field0, TRB_LEN(field2),
- GET_TD_SIZE(field2),
- GET_INTR_TARGET(field2),
- field3 & TRB_IDT ? 'D' : 'i',
- field3 & TRB_IOC ? 'I' : 'i',
- field3 & TRB_CHAIN ? 'C' : 'c',
- field3 & TRB_NO_SNOOP ? 'S' : 's',
- field3 & TRB_ISP ? 'I' : 'i',
- field3 & TRB_ENT ? 'E' : 'e',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "type '%s' Buffer %08x%08x length %ld TD size %ld "
+ "intr %ld flags %c:%c:%c:%c:%c:%c:%c",
+ cdnsp_trb_type_string(type),
+ field1, field0, TRB_LEN(field2),
+ GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ field3 & TRB_IDT ? 'D' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STATUS:
- ret += snprintf(str, size,
- "Buffer %08x%08x length %ld TD size %ld intr"
- "%ld type '%s' flags %c:%c:%c:%c",
- field1, field0, TRB_LEN(field2),
- GET_TD_SIZE(field2),
- GET_INTR_TARGET(field2),
- cdnsp_trb_type_string(type),
- field3 & TRB_IOC ? 'I' : 'i',
- field3 & TRB_CHAIN ? 'C' : 'c',
- field3 & TRB_ENT ? 'E' : 'e',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "Buffer %08x%08x length %ld TD size %ld intr"
+ "%ld type '%s' flags %c:%c:%c:%c",
+ field1, field0, TRB_LEN(field2),
+ GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ cdnsp_trb_type_string(type),
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_NORMAL:
case TRB_ISOC:
case TRB_EVENT_DATA:
case TRB_TR_NOOP:
- ret += snprintf(str, size,
- "type '%s' Buffer %08x%08x length %ld "
- "TD size %ld intr %ld "
- "flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
- cdnsp_trb_type_string(type),
- field1, field0, TRB_LEN(field2),
- GET_TD_SIZE(field2),
- GET_INTR_TARGET(field2),
- field3 & TRB_BEI ? 'B' : 'b',
- field3 & TRB_IDT ? 'T' : 't',
- field3 & TRB_IOC ? 'I' : 'i',
- field3 & TRB_CHAIN ? 'C' : 'c',
- field3 & TRB_NO_SNOOP ? 'S' : 's',
- field3 & TRB_ISP ? 'I' : 'i',
- field3 & TRB_ENT ? 'E' : 'e',
- field3 & TRB_CYCLE ? 'C' : 'c',
- !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
+ ret = snprintf(str, size,
+ "type '%s' Buffer %08x%08x length %ld "
+ "TD size %ld intr %ld "
+ "flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
+ cdnsp_trb_type_string(type),
+ field1, field0, TRB_LEN(field2),
+ GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ field3 & TRB_BEI ? 'B' : 'b',
+ field3 & TRB_IDT ? 'T' : 't',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c',
+ !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
break;
case TRB_CMD_NOOP:
case TRB_ENABLE_SLOT:
- ret += snprintf(str, size, "%s: flags %c",
- cdnsp_trb_type_string(type),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size, "%s: flags %c",
+ cdnsp_trb_type_string(type),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DISABLE_SLOT:
- ret += snprintf(str, size, "%s: slot %ld flags %c",
- cdnsp_trb_type_string(type),
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size, "%s: slot %ld flags %c",
+ cdnsp_trb_type_string(type),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ADDR_DEV:
- ret += snprintf(str, size,
- "%s: ctx %08x%08x slot %ld flags %c:%c",
- cdnsp_trb_type_string(type), field1, field0,
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_BSR ? 'B' : 'b',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ctx %08x%08x slot %ld flags %c:%c",
+ cdnsp_trb_type_string(type), field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_BSR ? 'B' : 'b',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_CONFIG_EP:
- ret += snprintf(str, size,
- "%s: ctx %08x%08x slot %ld flags %c:%c",
- cdnsp_trb_type_string(type), field1, field0,
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_DC ? 'D' : 'd',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ctx %08x%08x slot %ld flags %c:%c",
+ cdnsp_trb_type_string(type), field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_DC ? 'D' : 'd',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_EVAL_CONTEXT:
- ret += snprintf(str, size,
- "%s: ctx %08x%08x slot %ld flags %c",
- cdnsp_trb_type_string(type), field1, field0,
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ctx %08x%08x slot %ld flags %c",
+ cdnsp_trb_type_string(type), field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
case TRB_FLUSH_ENDPOINT:
- ret += snprintf(str, size,
- "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
- cdnsp_trb_type_string(type),
- ep_num, ep_id % 2 ? "out" : "in",
- TRB_TO_EP_INDEX(field3), field1, field0,
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
+ cdnsp_trb_type_string(type),
+ ep_num, ep_id % 2 ? "out" : "in",
+ TRB_TO_EP_INDEX(field3), field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STOP_RING:
- ret += snprintf(str, size,
- "%s: ep%d%s(%d) slot %ld sp %d flags %c",
- cdnsp_trb_type_string(type),
- ep_num, ep_id % 2 ? "out" : "in",
- TRB_TO_EP_INDEX(field3),
- TRB_TO_SLOT_ID(field3),
- TRB_TO_SUSPEND_PORT(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ep%d%s(%d) slot %ld sp %d flags %c",
+ cdnsp_trb_type_string(type),
+ ep_num, ep_id % 2 ? "out" : "in",
+ TRB_TO_EP_INDEX(field3),
+ TRB_TO_SLOT_ID(field3),
+ TRB_TO_SUSPEND_PORT(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
- ret += snprintf(str, size,
- "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
- cdnsp_trb_type_string(type),
- ep_num, ep_id % 2 ? "out" : "in",
- TRB_TO_EP_INDEX(field3), field1, field0,
- TRB_TO_STREAM_ID(field2),
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size,
+ "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
+ cdnsp_trb_type_string(type),
+ ep_num, ep_id % 2 ? "out" : "in",
+ TRB_TO_EP_INDEX(field3), field1, field0,
+ TRB_TO_STREAM_ID(field2),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
- ret += snprintf(str, size, "%s: slot %ld flags %c",
- cdnsp_trb_type_string(type),
- TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ ret = snprintf(str, size, "%s: slot %ld flags %c",
+ cdnsp_trb_type_string(type),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ENDPOINT_NRDY:
- temp = TRB_TO_HOST_STREAM(field2);
-
- ret += snprintf(str, size,
- "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
- cdnsp_trb_type_string(type),
- ep_num, ep_id % 2 ? "out" : "in",
- TRB_TO_EP_INDEX(field3), temp,
- temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
- temp == STREAM_REJECTED ? "(REJECTED)" : "",
- TRB_TO_DEV_STREAM(field0),
- field3 & TRB_STAT ? 'S' : 's',
- field3 & TRB_CYCLE ? 'C' : 'c');
+ temp = TRB_TO_HOST_STREAM(field2);
+
+ ret = snprintf(str, size,
+ "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
+ cdnsp_trb_type_string(type),
+ ep_num, ep_id % 2 ? "out" : "in",
+ TRB_TO_EP_INDEX(field3), temp,
+ temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
+ temp == STREAM_REJECTED ? "(REJECTED)" : "",
+ TRB_TO_DEV_STREAM(field0),
+ field3 & TRB_STAT ? 'S' : 's',
+ field3 & TRB_CYCLE ? 'C' : 'c');
break;
default:
- ret += snprintf(str, size,
- "type '%s' -> raw %08x %08x %08x %08x",
- cdnsp_trb_type_string(type),
- field0, field1, field2, field3);
+ ret = snprintf(str, size,
+ "type '%s' -> raw %08x %08x %08x %08x",
+ cdnsp_trb_type_string(type),
+ field0, field1, field2, field3);
}
+ if (ret >= size)
+ pr_info("CDNSP: buffer overflowed.\n");
+
return str;
}
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 5c9d07cc5410..c67715f6f756 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -1243,12 +1243,9 @@ static int cdnsp_run(struct cdnsp_device *pdev,
enum usb_device_speed speed)
{
u32 fs_speed = 0;
- u64 temp_64;
u32 temp;
int ret;
- temp_64 = cdnsp_read_64(&pdev->ir_set->erst_dequeue);
- temp_64 &= ~ERST_PTR_MASK;
temp = readl(&pdev->ir_set->irq_control);
temp &= ~IMOD_INTERVAL_MASK;
temp |= ((IMOD_DEFAULT_INTERVAL / 250) & IMOD_INTERVAL_MASK);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index f9ca5010f65b..dc6c96e04bcf 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -2152,7 +2152,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
{
/*
* host doesn't care B_SESSION_VALID event
- * so clear and disbale BSV irq
+ * so clear and disable BSV irq
*/
if (ci->is_otg)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index fa66e6e58792..6abb7294e919 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -139,30 +139,42 @@ MODULE_PARM_DESC(usbfs_memory_mb,
/* Hard limit, necessary to avoid arithmetic overflow */
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
-static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */
+static DEFINE_SPINLOCK(usbfs_memory_usage_lock);
+static u64 usbfs_memory_usage; /* Total memory currently allocated */
/* Check whether it's okay to allocate more memory for a transfer */
static int usbfs_increase_memory_usage(u64 amount)
{
- u64 lim;
+ u64 lim, total_mem;
+ unsigned long flags;
+ int ret;
lim = READ_ONCE(usbfs_memory_mb);
lim <<= 20;
- atomic64_add(amount, &usbfs_memory_usage);
-
- if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
- atomic64_sub(amount, &usbfs_memory_usage);
- return -ENOMEM;
- }
+ ret = 0;
+ spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
+ total_mem = usbfs_memory_usage + amount;
+ if (lim > 0 && total_mem > lim)
+ ret = -ENOMEM;
+ else
+ usbfs_memory_usage = total_mem;
+ spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
- return 0;
+ return ret;
}
/* Memory for a transfer is being deallocated */
static void usbfs_decrease_memory_usage(u64 amount)
{
- atomic64_sub(amount, &usbfs_memory_usage);
+ unsigned long flags;
+
+ spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
+ if (amount > usbfs_memory_usage)
+ usbfs_memory_usage = 0;
+ else
+ usbfs_memory_usage -= amount;
+ spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
}
static int connected(struct usb_dev_state *ps)
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index d630cccd2e6e..784466117c92 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -248,7 +248,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
hcd->rsrc_len, driver->description))
break;
}
- if (region == PCI_ROM_RESOURCE) {
+ if (region == PCI_STD_NUM_BARS) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto put_hcd;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 47a1c8bddf86..83b5aff25dd6 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2983,8 +2983,12 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
status);
}
- /* Check for disconnect or reset */
- if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+ /*
+ * Check for disconnect or reset, and bail out after several
+ * reset attempts to avoid warm reset loop.
+ */
+ if (status == 0 || status == -ENOTCONN || status == -ENODEV ||
+ (status == -EBUSY && i == PORT_RESET_TRIES - 1)) {
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_RESET);
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 50b2fc7fcc0e..bb1da35eb891 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -166,7 +166,7 @@ usb_acpi_get_companion_for_port(struct usb_port *port_dev)
if (!parent_handle)
return NULL;
- acpi_bus_get_device(parent_handle, &adev);
+ adev = acpi_fetch_acpi_dev(parent_handle);
port1 = port_dev->portnum;
}
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index d300ae3d9274..1306f4ec788d 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -82,6 +82,14 @@ static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
p->phy_utmi_width = 8;
}
+static void dwc2_set_socfpga_agilex_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+ p->no_clock_gating = true;
+}
+
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -239,6 +247,8 @@ const struct of_device_id dwc2_of_match_table[] = {
.data = dwc2_set_stm32mp15_fsotg_params },
{ .compatible = "st,stm32mp15-hsotg",
.data = dwc2_set_stm32mp15_hsotg_params },
+ { .compatible = "intel,socfpga-agilex-hsotg",
+ .data = dwc2_set_socfpga_agilex_params },
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f4c09951b517..18adddfba3da 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -347,17 +347,64 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
*/
static void dwc3_ref_clk_period(struct dwc3 *dwc)
{
+ unsigned long period;
+ unsigned long fladj;
+ unsigned long decr;
+ unsigned long rate;
u32 reg;
- if (dwc->ref_clk_per == 0)
+ if (dwc->ref_clk) {
+ rate = clk_get_rate(dwc->ref_clk);
+ if (!rate)
+ return;
+ period = NSEC_PER_SEC / rate;
+ } else if (dwc->ref_clk_per) {
+ period = dwc->ref_clk_per;
+ rate = NSEC_PER_SEC / period;
+ } else {
return;
+ }
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
- reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
+ reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
-}
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
+ return;
+
+ /*
+ * The calculation below is
+ *
+ * 125000 * (NSEC_PER_SEC / (rate * period) - 1)
+ *
+ * but rearranged for fixed-point arithmetic. The division must be
+ * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
+ * neither does rate * period).
+ *
+ * Note that rate * period ~= NSEC_PER_SECOND, minus the number of
+ * nanoseconds of error caused by the truncation which happened during
+ * the division when calculating rate or period (whichever one was
+ * derived from the other). We first calculate the relative error, then
+ * scale it to units of 8 ppm.
+ */
+ fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
+ fladj -= 125000;
+
+ /*
+ * The documented 240MHz constant is scaled by 2 to get PLS1 as well.
+ */
+ decr = 480000000 / rate;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
+ & ~DWC3_GFLADJ_240MHZDECR
+ & ~DWC3_GFLADJ_240MHZDECR_PLS1;
+ reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
+ | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
+ | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+}
/**
* dwc3_free_one_event_buffer - Frees one event buffer
@@ -745,6 +792,38 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
return 0;
}
+static int dwc3_clk_enable(struct dwc3 *dwc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(dwc->bus_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dwc->ref_clk);
+ if (ret)
+ goto disable_bus_clk;
+
+ ret = clk_prepare_enable(dwc->susp_clk);
+ if (ret)
+ goto disable_ref_clk;
+
+ return 0;
+
+disable_ref_clk:
+ clk_disable_unprepare(dwc->ref_clk);
+disable_bus_clk:
+ clk_disable_unprepare(dwc->bus_clk);
+ return ret;
+}
+
+static void dwc3_clk_disable(struct dwc3 *dwc)
+{
+ clk_disable_unprepare(dwc->susp_clk);
+ clk_disable_unprepare(dwc->ref_clk);
+ clk_disable_unprepare(dwc->bus_clk);
+}
+
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
@@ -758,7 +837,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
@@ -1605,25 +1684,31 @@ static int dwc3_probe(struct platform_device *pdev)
return PTR_ERR(dwc->reset);
if (dev->of_node) {
- ret = devm_clk_bulk_get_all(dev, &dwc->clks);
- if (ret == -EPROBE_DEFER)
- return ret;
/*
* Clocks are optional, but new DT platforms should support all
* clocks as required by the DT-binding.
*/
- if (ret < 0)
- dwc->num_clks = 0;
- else
- dwc->num_clks = ret;
-
+ dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
+ if (IS_ERR(dwc->bus_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+
+ dwc->ref_clk = devm_clk_get_optional(dev, "ref");
+ if (IS_ERR(dwc->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+
+ dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
+ if (IS_ERR(dwc->susp_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
}
ret = reset_control_deassert(dwc->reset);
if (ret)
return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
+ ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@@ -1711,7 +1796,7 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
@@ -1755,7 +1840,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
+ ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@@ -1766,7 +1851,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e1cc3f7398fb..eb9c1efced05 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -388,6 +388,9 @@
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
+#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
+#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
@@ -978,8 +981,9 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
- * @clks: array of clocks
- * @num_clks: number of clocks
+ * @bus_clk: clock for accessing the registers
+ * @ref_clk: reference clock
+ * @susp_clk: clock used when the SS phy is in low power (S3) state
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
@@ -1134,8 +1138,9 @@ struct dwc3 {
struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
- struct clk_bulk_data *clks;
- int num_clks;
+ struct clk *bus_clk;
+ struct clk *ref_clk;
+ struct clk *susp_clk;
struct reset_control *reset;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index d7f76835137f..25f322e62d3f 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -9,6 +9,7 @@
#include <linux/extcon.h>
#include <linux/of_graph.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@@ -559,6 +560,18 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
if (IS_ERR(dwc->role_sw))
return PTR_ERR(dwc->role_sw);
+ if (IS_ENABLED(CONFIG_OF)) {
+ /* populate connector entry */
+ int ret = devm_of_platform_populate(dwc->dev);
+
+ if (ret) {
+ usb_role_switch_unregister(dwc->role_sw);
+ dwc->role_sw = NULL;
+ dev_err(dwc->dev, "DWC3 platform devices creation failed: %i\n", ret);
+ return ret;
+ }
+ }
+
dwc3_set_mode(dwc, mode);
return 0;
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 1922fd02043c..4585ee3a444a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -919,12 +919,12 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
data_len, ret);
data_len -= ret;
- buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
+ buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf->length = data_len;
buf->data = buf->storage;
- memcpy(buf->storage, data + ret, data_len);
+ memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
/*
* At this point read_buffer is NULL or READ_BUFFER_DROP (if
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 46dd11dcb3a8..ba899ca8ee70 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -1188,6 +1188,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
int msf = common->cmnd[1] & 0x02;
int start_track = common->cmnd[6];
u8 *buf = (u8 *)bh->buf;
+ u8 format;
+ int i, len;
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
@@ -1195,18 +1197,62 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
return -EINVAL;
}
- memset(buf, 0, 20);
- buf[1] = (20-2); /* TOC data length */
- buf[2] = 1; /* First track number */
- buf[3] = 1; /* Last track number */
- buf[5] = 0x16; /* Data track, copying allowed */
- buf[6] = 0x01; /* Only track is number 1 */
- store_cdrom_address(&buf[8], msf, 0);
+ format = common->cmnd[2] & 0xf;
+ /*
+ * Check if CDB is old style SFF-8020i
+ * i.e. format is in 2 MSBs of byte 9
+ * Mac OS-X host sends us this.
+ */
+ if (format == 0)
+ format = (common->cmnd[9] >> 6) & 0x3;
+
+ switch (format) {
+ case 0:
+ /* Formatted TOC */
+ len = 4 + 2*8; /* 4 byte header + 2 descriptors */
+ memset(buf, 0, len);
+ buf[1] = len - 2; /* TOC Length excludes length field */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+ return len;
+
+ case 2:
+ /* Raw TOC */
+ len = 4 + 3*11; /* 4 byte header + 3 descriptors */
+ memset(buf, 0, len); /* Header + A0, A1 & A2 descriptors */
+ buf[1] = len - 2; /* TOC Length excludes length field */
+ buf[2] = 1; /* First complete session */
+ buf[3] = 1; /* Last complete session */
+
+ buf += 4;
+ /* fill in A0, A1 and A2 points */
+ for (i = 0; i < 3; i++) {
+ buf[0] = 1; /* Session number */
+ buf[1] = 0x16; /* Data track, copying allowed */
+ /* 2 - Track number 0 -> TOC */
+ buf[3] = 0xA0 + i; /* A0, A1, A2 point */
+ /* 4, 5, 6 - Min, sec, frame is zero */
+ buf[8] = 1; /* Pmin: last track number */
+ buf += 11; /* go to next track descriptor */
+ }
+ buf -= 11; /* go back to A2 descriptor */
- buf[13] = 0x16; /* Lead-out track is data */
- buf[14] = 0xAA; /* Lead-out track number */
- store_cdrom_address(&buf[16], msf, curlun->num_sectors);
- return 20;
+ /* For A2, 7, 8, 9, 10 - zero, Pmin, Psec, Pframe of Lead out */
+ store_cdrom_address(&buf[7], msf, curlun->num_sectors);
+ return len;
+
+ default:
+ /* Multi-session, PMA, ATIP, CD-TEXT not supported/required */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
}
static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
@@ -1944,7 +1990,7 @@ static int do_scsi_command(struct fsg_common *common)
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]);
reply = check_command(common, 10, DATA_DIR_TO_HOST,
- (7<<6) | (1<<1), 1,
+ (0xf<<6) | (3<<1), 1,
"READ TOC");
if (reply == 0)
reply = do_read_toc(common, bh);
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 068ed8417e5a..0bebbdf3f213 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -668,10 +668,8 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
{
struct f_phonet *fp;
struct f_phonet_opts *opts;
- int size;
- size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
- fp = kzalloc(size, GFP_KERNEL);
+ fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
if (!fp)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 1ed8ff0ac2d3..a9480b9e312e 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -345,6 +345,10 @@ static void gser_free(struct usb_function *f)
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gser *gser = func_to_gser(f);
+
+ /* Ensure port is disconnected before unbinding */
+ gserial_disconnect(&gser->port);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 03f50643fbba..6f0e1d803dc2 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -3,6 +3,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@@ -42,6 +43,9 @@ struct f_uac1 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ int ctl_id; /* EP id */
+ int c_srate; /* current capture srate */
+ int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
+#define uac_format_type_i_discrete_descriptor \
+ uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
-static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(1),
};
-static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -303,7 +309,7 @@ enum {
};
static struct usb_string strings_uac1[] = {
- [STR_AC_IF].s = "AC Interface",
+ /* [STR_AC_IF].s = DYNAMIC, */
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
@@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
+static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac1 *uac1 = func_to_uac1(fn);
+ u8 *buf = (u8 *)req->buf;
+ u32 val = 0;
+
+ if (req->actual != 3) {
+ WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
+ return;
+ }
+
+ val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+ if (uac1->ctl_id == (USB_DIR_IN | 2)) {
+ uac1->p_srate = val;
+ u_audio_set_playback_srate(agdev, uac1->p_srate);
+ } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+ uac1->c_srate = val;
+ u_audio_set_capture_srate(agdev, uac1->c_srate);
+ }
+}
+
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
@@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_SET_CUR:
+ case UAC_SET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ cdev->gadget->ep0->driver_data = f;
+ uac1->ctl_id = ep;
+ req->complete = uac_cs_attr_sample_rate;
+ }
value = len;
break;
+ }
case UAC_SET_MIN:
break;
@@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
+ u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u8 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
+ u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_GET_CUR:
+ case UAC_GET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ if (ep == (USB_DIR_IN | 2))
+ val = uac1->p_srate;
+ else if (ep == (USB_DIR_OUT | 1))
+ val = uac1->c_srate;
+ buf[2] = (val >> 16) & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[0] = val & 0xff;
+ }
+ value = len;
+ break;
+ }
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
@@ -905,6 +961,14 @@ static void f_audio_disable(struct usb_function *f)
usb_ep_disable(uac1->int_ep);
}
+static void
+f_audio_suspend(struct usb_function *f)
+{
+ struct f_uac1 *uac1 = func_to_uac1(f);
+
+ u_audio_suspend(&uac1->g_audio);
+}
+
/*-------------------------------------------------------------------------*/
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
{
@@ -1074,10 +1138,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1118,10 +1182,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- u8 *sam_freq;
- int rate;
int ba_iface_id;
int status;
+ int idx, i;
status = f_audio_validate_opts(audio, dev);
if (status)
@@ -1129,6 +1192,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ strings_uac1[STR_AC_IF].s = audio_opts->function_name;
+
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1213,12 +1278,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Set sample rates */
- rate = audio_opts->c_srate;
- sam_freq = as_out_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
- rate = audio_opts->p_srate;
- sam_freq = as_in_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->c_srates[i] == 0)
+ break;
+ memcpy(as_out_type_i_desc.tSamFreq[idx++],
+ &audio_opts->c_srates[i], 3);
+ }
+ as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_out_type_i_desc.bSamFreqType = idx;
+
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->p_srates[i] == 0)
+ break;
+ memcpy(as_in_type_i_desc.tSamFreq[idx++],
+ &audio_opts->p_srates[i], 3);
+ }
+ as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_in_type_i_desc.bSamFreqType = idx;
+ uac1->p_srate = audio_opts->p_srates[0];
+ uac1->c_srate = audio_opts->c_srates[0];
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -1297,7 +1375,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srate = audio_opts->c_srate;
+ memcpy(audio->params.c_srates, audio_opts->c_srates,
+ sizeof(audio->params.c_srates));
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1309,7 +1388,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srate = audio_opts->p_srate;
+ memcpy(audio->params.p_srates, audio_opts->p_srates,
+ sizeof(audio->params.p_srates));
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
@@ -1414,11 +1494,106 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
+#define UAC1_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
+#define UAC1_ATTRIBUTE_STRING(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ "%s", page); \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
UAC1_ATTRIBUTE(u32, c_chmask);
-UAC1_ATTRIBUTE(u32, c_srate);
+UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
-UAC1_ATTRIBUTE(u32, p_srate);
+UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);
@@ -1433,6 +1608,7 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+UAC1_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
@@ -1455,6 +1631,8 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_volume_max,
&f_uac1_opts_attr_c_volume_res,
+ &f_uac1_opts_attr_function_name,
+
NULL,
};
@@ -1487,10 +1665,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
- opts->c_srate = UAC1_DEF_CSRATE;
+ opts->c_srates[0] = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
- opts->p_srate = UAC1_DEF_PSRATE;
+ opts->p_srates[0] = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
@@ -1506,6 +1684,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_volume_res = UAC1_DEF_RES_DB;
opts->req_number = UAC1_DEF_REQ_NUM;
+
+ snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+
return &opts->func_inst;
}
@@ -1562,6 +1743,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
uac1->g_audio.func.get_alt = f_audio_get_alt;
uac1->g_audio.func.setup = f_audio_setup;
uac1->g_audio.func.disable = f_audio_disable;
+ uac1->g_audio.func.suspend = f_audio_suspend;
uac1->g_audio.func.free_func = f_audio_free;
return &uac1->g_audio.func;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 097a709549d6..1905a8d8e0c9 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -70,6 +70,8 @@ struct f_uac2 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ /* transient state, only valid during handling of a single control request */
+ int clock_id;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -104,14 +106,11 @@ enum {
STR_AS_IN_ALT1,
};
-static char clksrc_in[8];
-static char clksrc_out[8];
-
static struct usb_string strings_fn[] = {
- [STR_ASSOC].s = "Source/Sink",
+ /* [STR_ASSOC].s = DYNAMIC, */
[STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = clksrc_in,
- [STR_CLKSRC_OUT].s = clksrc_out,
+ [STR_CLKSRC_IN].s = "Input Clock",
+ [STR_CLKSRC_OUT].s = "Output Clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
@@ -125,6 +124,16 @@ static struct usb_string strings_fn[] = {
{ },
};
+static const char *const speed_names[] = {
+ [USB_SPEED_UNKNOWN] = "UNKNOWN",
+ [USB_SPEED_LOW] = "LS",
+ [USB_SPEED_FULL] = "FS",
+ [USB_SPEED_HIGH] = "HS",
+ [USB_SPEED_WIRELESS] = "W",
+ [USB_SPEED_SUPER] = "SS",
+ [USB_SPEED_SUPER_PLUS] = "SS+",
+};
+
static struct usb_gadget_strings str_fn = {
.language = 0x0409, /* en-us */
.strings = strings_fn,
@@ -166,7 +175,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -178,7 +187,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -344,7 +353,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epout_desc = {
@@ -354,7 +363,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
.bEndpointAddress = USB_DIR_OUT,
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
@@ -478,7 +487,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epin_desc = {
@@ -488,7 +497,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
@@ -634,44 +643,50 @@ struct cntrl_cur_lay3 {
__le32 dCUR;
};
-struct cntrl_range_lay3 {
- __le16 wNumSubRanges;
+struct cntrl_subrange_lay3 {
__le32 dMIN;
__le32 dMAX;
__le32 dRES;
} __packed;
-static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
- struct usb_endpoint_descriptor *ep_desc,
- enum usb_device_speed speed, bool is_playback)
-{
- int chmask, srate, ssize;
- u16 max_size_bw, max_size_ep;
- unsigned int factor;
+#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) \
+ + le16_to_cpu(c.wNumSubRanges) \
+ * sizeof(struct cntrl_subrange_lay3))
- switch (speed) {
- case USB_SPEED_FULL:
- max_size_ep = 1023;
- factor = 1000;
- break;
+#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
+ struct cntrl_ranges_lay3_##k { \
+ __le16 wNumSubRanges; \
+ struct cntrl_subrange_lay3 r[n]; \
+} __packed
- case USB_SPEED_HIGH:
- case USB_SPEED_SUPER:
- max_size_ep = 1024;
- factor = 8000;
- break;
+DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
- default:
- return -EINVAL;
+static int get_max_srate(const int *srates)
+{
+ int i, max_srate = 0;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] > max_srate)
+ max_srate = srates[i];
}
+ return max_srate;
+}
+
+static int get_max_bw_for_bint(const struct f_uac2_opts *uac2_opts,
+ u8 bint, unsigned int factor, bool is_playback)
+{
+ int chmask, srate, ssize;
+ u16 max_size_bw;
if (is_playback) {
chmask = uac2_opts->p_chmask;
- srate = uac2_opts->p_srate;
+ srate = get_max_srate(uac2_opts->p_srates);
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
- srate = uac2_opts->c_srate;
+ srate = get_max_srate(uac2_opts->c_srates);
ssize = uac2_opts->c_ssize;
}
@@ -681,14 +696,76 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
srate = srate * (1000 + uac2_opts->fb_max) / 1000;
// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
max_size_bw = num_channels(chmask) * ssize *
- (DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))));
+ (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))));
} else {
// adding 1 frame provision for Win10
max_size_bw = num_channels(chmask) * ssize *
- (DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1);
+ (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1);
}
- ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
- max_size_ep));
+ return max_size_bw;
+}
+
+static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts,
+ struct usb_endpoint_descriptor *ep_desc,
+ enum usb_device_speed speed, bool is_playback)
+{
+ u16 max_size_bw, max_size_ep;
+ u8 bint, opts_bint;
+ char *dir;
+
+ switch (speed) {
+ case USB_SPEED_FULL:
+ max_size_ep = 1023;
+ // fixed
+ bint = ep_desc->bInterval;
+ max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
+ break;
+
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ max_size_ep = 1024;
+ if (is_playback)
+ opts_bint = uac2_opts->p_hs_bint;
+ else
+ opts_bint = uac2_opts->c_hs_bint;
+
+ if (opts_bint > 0) {
+ /* fixed bint */
+ bint = opts_bint;
+ max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
+ } else {
+ /* checking bInterval from 4 to 1 whether the required bandwidth fits */
+ for (bint = 4; bint > 0; --bint) {
+ max_size_bw = get_max_bw_for_bint(
+ uac2_opts, bint, 8000, is_playback);
+ if (max_size_bw <= max_size_ep)
+ break;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (is_playback)
+ dir = "Playback";
+ else
+ dir = "Capture";
+
+ if (max_size_bw <= max_size_ep)
+ dev_dbg(dev,
+ "%s %s: Would use wMaxPacketSize %d and bInterval %d\n",
+ speed_names[speed], dir, max_size_bw, bint);
+ else {
+ dev_warn(dev,
+ "%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n",
+ speed_names[speed], dir, max_size_bw, bint, max_size_ep);
+ max_size_bw = max_size_ep;
+ }
+
+ ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
+ ep_desc->bInterval = bint;
return 0;
}
@@ -896,50 +973,45 @@ static void setup_descriptor(struct f_uac2_opts *opts)
static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
{
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
-
- if (!opts->p_chmask && !opts->c_chmask) {
- dev_err(dev, "Error: no playback and capture channels\n");
- return -EINVAL;
- } else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) {
- dev_err(dev, "Error: unsupported playback channels mask\n");
- return -EINVAL;
- } else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) {
- dev_err(dev, "Error: unsupported capture channels mask\n");
- return -EINVAL;
- } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
- dev_err(dev, "Error: incorrect playback sample size\n");
- return -EINVAL;
- } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
- dev_err(dev, "Error: incorrect capture sample size\n");
- return -EINVAL;
- } else if (!opts->p_srate) {
- dev_err(dev, "Error: incorrect playback sampling rate\n");
+ const char *msg = NULL;
+
+ if (!opts->p_chmask && !opts->c_chmask)
+ msg = "no playback and capture channels";
+ else if (opts->p_chmask & ~UAC2_CHANNEL_MASK)
+ msg = "unsupported playback channels mask";
+ else if (opts->c_chmask & ~UAC2_CHANNEL_MASK)
+ msg = "unsupported capture channels mask";
+ else if ((opts->p_ssize < 1) || (opts->p_ssize > 4))
+ msg = "incorrect playback sample size";
+ else if ((opts->c_ssize < 1) || (opts->c_ssize > 4))
+ msg = "incorrect capture sample size";
+ else if (!opts->p_srates[0])
+ msg = "incorrect playback sampling rate";
+ else if (!opts->c_srates[0])
+ msg = "incorrect capture sampling rate";
+
+ else if (opts->p_volume_max <= opts->p_volume_min)
+ msg = "incorrect playback volume max/min";
+ else if (opts->c_volume_max <= opts->c_volume_min)
+ msg = "incorrect capture volume max/min";
+ else if (opts->p_volume_res <= 0)
+ msg = "negative/zero playback volume resolution";
+ else if (opts->c_volume_res <= 0)
+ msg = "negative/zero capture volume resolution";
+
+ else if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res)
+ msg = "incorrect playback volume resolution";
+ else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res)
+ msg = "incorrect capture volume resolution";
+
+ else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4))
+ msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
+ else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4))
+ msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
+
+ if (msg) {
+ dev_err(dev, "Error: %s\n", msg);
return -EINVAL;
- } else if (!opts->c_srate) {
- dev_err(dev, "Error: incorrect capture sampling rate\n");
- return -EINVAL;
- }
-
- if (opts->p_volume_max <= opts->p_volume_min) {
- dev_err(dev, "Error: incorrect playback volume max/min\n");
- return -EINVAL;
- } else if (opts->c_volume_max <= opts->c_volume_min) {
- dev_err(dev, "Error: incorrect capture volume max/min\n");
- return -EINVAL;
- } else if (opts->p_volume_res <= 0) {
- dev_err(dev, "Error: negative/zero playback volume resolution\n");
- return -EINVAL;
- } else if (opts->c_volume_res <= 0) {
- dev_err(dev, "Error: negative/zero capture volume resolution\n");
- return -EINVAL;
- }
-
- if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
- dev_err(dev, "Error: incorrect playback volume resolution\n");
- return -EINVAL;
- } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
- dev_err(dev, "Error: incorrect capture volume resolution\n");
- return -EINVAL;
}
return 0;
@@ -961,6 +1033,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
if (ret)
return ret;
+ strings_fn[STR_ASSOC].s = uac2_opts->function_name;
+
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1037,9 +1111,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
*bma = cpu_to_le32(control);
}
- snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
- snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
-
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -1103,44 +1174,49 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
std_ac_if_desc.bNumEndpoints = 1;
}
+ hs_epin_desc.bInterval = uac2_opts->p_hs_bint;
+ ss_epin_desc.bInterval = uac2_opts->p_hs_bint;
+ hs_epout_desc.bInterval = uac2_opts->c_hs_bint;
+ ss_epout_desc.bInterval = uac2_opts->c_hs_bint;
+
/* Calculate wMaxPacketSize according to audio bandwidth */
- ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epin_desc,
+ USB_SPEED_FULL, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epout_desc,
+ USB_SPEED_FULL, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epin_desc,
+ USB_SPEED_HIGH, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epout_desc,
+ USB_SPEED_HIGH, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epin_desc,
+ USB_SPEED_SUPER, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc,
+ USB_SPEED_SUPER, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
@@ -1209,7 +1285,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
- agdev->params.p_srate = uac2_opts->p_srate;
+ memcpy(agdev->params.p_srates, uac2_opts->p_srates,
+ sizeof(agdev->params.p_srates));
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@@ -1220,7 +1297,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srate = uac2_opts->c_srate;
+ memcpy(agdev->params.c_srates, uac2_opts->c_srates,
+ sizeof(agdev->params.c_srates));
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
@@ -1411,6 +1489,14 @@ afunc_disable(struct usb_function *fn)
usb_ep_disable(uac2->int_ep);
}
+static void
+afunc_suspend(struct usb_function *fn)
+{
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+
+ u_audio_suspend(&uac2->g_audio);
+}
+
static int
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
@@ -1423,10 +1509,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
+ u32 p_srate, c_srate;
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
+ u_audio_get_playback_srate(agdev, &p_srate);
+ u_audio_get_capture_srate(agdev, &c_srate);
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
@@ -1500,28 +1586,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
-
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- struct cntrl_range_lay3 r;
+ struct cntrl_ranges_lay3_srates rs;
+ int i;
+ int wNumSubRanges = 0;
+ int srate;
+ int *srates;
if (entity_id == USB_IN_CLK_ID)
- r.dMIN = cpu_to_le32(p_srate);
+ srates = opts->p_srates;
else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = cpu_to_le32(c_srate);
+ srates = opts->c_srates;
else
return -EOPNOTSUPP;
-
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = cpu_to_le16(1);
-
- value = min_t(unsigned int, w_length, sizeof(r));
- memcpy(req->buf, &r, value);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ srate = srates[i];
+ if (srate == 0)
+ break;
+
+ rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dRES = 0;
+ wNumSubRanges++;
+ dev_dbg(&agdev->gadget->dev,
+ "%s(): clk %d: rate ID %d: %d\n",
+ __func__, entity_id, wNumSubRanges, srate);
+ }
+ rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
+ value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
+ dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
+ __func__, rs.wNumSubRanges, value);
+ memcpy(req->buf, &rs, value);
} else {
dev_err(&agdev->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
@@ -1580,6 +1677,25 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
+static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+ u32 val;
+
+ if (req->actual != 4)
+ return;
+
+ val = le32_to_cpu(*((__le32 *)req->buf));
+ dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
+ if (uac2->clock_id == USB_IN_CLK_ID) {
+ u_audio_set_playback_srate(agdev, val);
+ } else if (uac2->clock_id == USB_OUT_CLK_ID) {
+ u_audio_set_capture_srate(agdev, val);
+ }
+}
+
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -1631,6 +1747,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ struct usb_composite_dev *cdev = fn->config->cdev;
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
@@ -1640,10 +1757,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
+ u8 clock_id = w_index >> 8;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ dev_dbg(&agdev->gadget->dev,
+ "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
+ cdev->gadget->ep0->driver_data = fn;
+ uac2->clock_id = clock_id;
+ req->complete = uac2_cs_control_sam_freq;
return w_length;
+ }
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
@@ -1731,10 +1855,12 @@ static struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
+#define uac2_kstrtou8 kstrtou8
#define uac2_kstrtou32 kstrtou32
#define uac2_kstrtos16 kstrtos16
#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
+static const char *u8_fmt = "%u\n";
static const char *u32_fmt = "%u\n";
static const char *s16_fmt = "%hd\n";
static const char *bool_fmt = "%u\n";
@@ -1836,13 +1962,110 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
+#define UAC2_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
+#define UAC2_ATTRIBUTE_STRING(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ "%s", page); \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
UAC2_ATTRIBUTE(u32, p_chmask);
-UAC2_ATTRIBUTE(u32, p_srate);
+UAC2_RATE_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(u32, p_ssize);
+UAC2_ATTRIBUTE(u8, p_hs_bint);
UAC2_ATTRIBUTE(u32, c_chmask);
-UAC2_ATTRIBUTE(u32, c_srate);
+UAC2_RATE_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(u32, c_ssize);
+UAC2_ATTRIBUTE(u8, c_hs_bint);
UAC2_ATTRIBUTE(u32, req_number);
UAC2_ATTRIBUTE(bool, p_mute_present);
@@ -1857,14 +2080,17 @@ UAC2_ATTRIBUTE(s16, c_volume_min);
UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
+UAC2_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
&f_uac2_opts_attr_p_ssize,
+ &f_uac2_opts_attr_p_hs_bint,
&f_uac2_opts_attr_c_chmask,
&f_uac2_opts_attr_c_srate,
&f_uac2_opts_attr_c_ssize,
+ &f_uac2_opts_attr_c_hs_bint,
&f_uac2_opts_attr_c_sync,
&f_uac2_opts_attr_req_number,
&f_uac2_opts_attr_fb_max,
@@ -1881,6 +2107,8 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_max,
&f_uac2_opts_attr_c_volume_res,
+ &f_uac2_opts_attr_function_name,
+
NULL,
};
@@ -1913,11 +2141,13 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC2_DEF_PCHMASK;
- opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_srates[0] = UAC2_DEF_PSRATE;
opts->p_ssize = UAC2_DEF_PSSIZE;
+ opts->p_hs_bint = UAC2_DEF_PHSBINT;
opts->c_chmask = UAC2_DEF_CCHMASK;
- opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_srates[0] = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
+ opts->c_hs_bint = UAC2_DEF_CHSBINT;
opts->c_sync = UAC2_DEF_CSYNC;
opts->p_mute_present = UAC2_DEF_MUTE_PRESENT;
@@ -1934,6 +2164,9 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = FBACK_FAST_MAX;
+
+ snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
+
return &opts->func_inst;
}
@@ -1985,6 +2218,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
uac2->g_audio.func.set_alt = afunc_set_alt;
uac2->g_audio.func.get_alt = afunc_get_alt;
uac2->g_audio.func.disable = afunc_disable;
+ uac2->g_audio.func.suspend = afunc_suspend;
uac2->g_audio.func.setup = afunc_setup;
uac2->g_audio.func.free_func = afunc_free;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 4561d7a183ff..2bb569895a90 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -32,6 +32,7 @@ enum {
UAC_P_PITCH_CTRL,
UAC_MUTE_CTRL,
UAC_VOLUME_CTRL,
+ UAC_RATE_CTRL,
};
/* Runtime data params for one stream */
@@ -62,6 +63,10 @@ struct uac_rtd_params {
s16 volume;
int mute;
+ struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
+ int srate; /* selected samplerate */
+ int active; /* playback/capture running */
+
spinlock_t lock; /* lock for control transfers */
};
@@ -150,8 +155,6 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
- struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
unsigned int frames, p_pktsize;
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
residue_frames_mil, div_result;
@@ -196,15 +199,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
*/
unsigned long long p_interval_mil = uac->p_interval * 1000000ULL;
- pitched_rate_mil = (unsigned long long)
- params->p_srate * prm->pitch;
+ pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch;
div_result = pitched_rate_mil;
do_div(div_result, uac->p_interval);
do_div(div_result, 1000000);
frames = (unsigned int) div_result;
pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",
- params->p_srate, prm->pitch, p_interval_mil, frames);
+ prm->srate, prm->pitch, p_interval_mil, frames);
p_pktsize = min_t(unsigned int,
uac->p_framesize * frames,
@@ -281,7 +283,6 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
int status = req->status;
/* i/f shutting down */
@@ -303,7 +304,7 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
__func__, status, req->actual, req->length);
u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req->buf);
if (usb_ep_queue(ep, req, GFP_ATOMIC))
@@ -387,16 +388,14 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct g_audio *audio_dev;
struct uac_params *params;
+ struct uac_rtd_params *prm;
int p_ssize, c_ssize;
- int p_srate, c_srate;
int p_chmask, c_chmask;
audio_dev = uac->audio_dev;
params = &audio_dev->params;
p_ssize = params->p_ssize;
c_ssize = params->c_ssize;
- p_srate = params->p_srate;
- c_srate = params->c_srate;
p_chmask = params->p_chmask;
c_chmask = params->c_chmask;
uac->p_residue_mil = 0;
@@ -404,19 +403,18 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
runtime->hw = uac_pcm_hardware;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw.rate_min = p_srate;
runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
runtime->hw.channels_min = num_channels(p_chmask);
- runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
- / runtime->hw.periods_min;
+ prm = &uac->p_prm;
} else {
- runtime->hw.rate_min = c_srate;
runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
runtime->hw.channels_min = num_channels(c_chmask);
- runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
- / runtime->hw.periods_min;
+ prm = &uac->c_prm;
}
+ runtime->hw.period_bytes_min = 2 * prm->max_psize
+ / runtime->hw.periods_min;
+ runtime->hw.rate_min = prm->srate;
runtime->hw.rate_max = runtime->hw.rate_min;
runtime->hw.channels_max = runtime->hw.channels_min;
@@ -493,6 +491,99 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
+static void set_active(struct uac_rtd_params *prm, bool active)
+{
+ // notifying through the Rate ctrl
+ struct snd_kcontrol *kctl = prm->snd_kctl_rate;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ if (prm->active != active) {
+ prm->active = active;
+ snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &kctl->id);
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+}
+
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
+{
+ struct uac_params *params = &audio_dev->params;
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ int i;
+ unsigned long flags;
+
+ dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ prm = &uac->c_prm;
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->c_srates[i] == srate) {
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+ }
+ if (params->c_srates[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
+
+int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ prm = &uac->c_prm;
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_capture_srate);
+
+int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
+{
+ struct uac_params *params = &audio_dev->params;
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ int i;
+ unsigned long flags;
+
+ dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ prm = &uac->p_prm;
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->p_srates[i] == srate) {
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+ }
+ if (params->p_srates[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
+
+int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ prm = &uac->p_prm;
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_playback_srate);
+
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
@@ -504,8 +595,9 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
int req_len, i;
- ep = audio_dev->out_ep;
prm = &uac->c_prm;
+ dev_dbg(dev, "start capture with rate %d\n", prm->srate);
+ ep = audio_dev->out_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
req_len = ep->maxpacket;
@@ -531,6 +623,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ set_active(&uac->c_prm, true);
+
ep_fback = audio_dev->in_ep_fback;
if (!ep_fback)
return 0;
@@ -562,7 +656,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
*/
prm->pitch = 1000000;
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req_fback->buf);
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
@@ -576,6 +670,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ set_active(&uac->c_prm, false);
if (audio_dev->in_ep_fback)
free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
free_ep(&uac->c_prm, audio_dev->out_ep);
@@ -596,8 +691,9 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
- ep = audio_dev->in_ep;
prm = &uac->p_prm;
+ dev_dbg(dev, "start playback with rate %d\n", prm->srate);
+ ep = audio_dev->in_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
ep_desc = ep->desc;
@@ -618,7 +714,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
p_pktsize = min_t(unsigned int,
uac->p_framesize *
- (params->p_srate / uac->p_interval),
+ (prm->srate / uac->p_interval),
ep->maxpacket);
req_len = p_pktsize;
@@ -646,6 +742,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ set_active(&uac->p_prm, true);
+
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_start_playback);
@@ -654,10 +752,20 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ set_active(&uac->p_prm, false);
free_ep(&uac->p_prm, audio_dev->in_ep);
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+void u_audio_suspend(struct g_audio *audio_dev)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+
+ set_active(&uac->p_prm, false);
+ set_active(&uac->c_prm, false);
+}
+EXPORT_SYMBOL_GPL(u_audio_suspend);
+
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
@@ -943,6 +1051,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
+static int get_max_srate(const int *srates)
+{
+ int i, max_srate = 0;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] > max_srate)
+ max_srate = srates[i];
+ }
+ return max_srate;
+}
+
+static int get_min_srate(const int *srates)
+{
+ int i, min_srate = INT_MAX;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] < min_srate)
+ min_srate = srates[i];
+ }
+ return min_srate;
+}
+
+static int u_audio_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const int *srates;
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+
+ if (prm == &uac->c_prm)
+ srates = params->c_srates;
+ else
+ srates = params->p_srates;
+ uinfo->value.integer.min = get_min_srate(srates);
+ uinfo->value.integer.max = get_max_srate(srates);
+ return 0;
+}
+
+static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ if (prm->active)
+ ucontrol->value.integer.value[0] = prm->srate;
+ else
+ /* not active: reporting zero rate */
+ ucontrol->value.integer.value[0] = 0;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+}
static struct snd_kcontrol_new u_audio_controls[] = {
[UAC_FBACK_CTRL] {
@@ -973,6 +1143,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
+ [UAC_RATE_CTRL] {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "", /* will be filled later */
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = u_audio_rate_info,
+ .get = u_audio_rate_get,
+ },
};
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@@ -1005,6 +1182,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->c_prm.uac = uac;
prm->max_psize = g_audio->out_ep_maxpsize;
+ prm->srate = params->c_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@@ -1029,6 +1207,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->p_prm.uac = uac;
prm->max_psize = g_audio->in_ep_maxpsize;
+ prm->srate = params->p_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@@ -1186,6 +1365,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
prm->volume_min = fu->volume_min;
prm->volume_res = fu->volume_res;
}
+
+ /* Add rate control */
+ snprintf(ctrl_name, sizeof(ctrl_name),
+ "%s Rate", direction);
+ u_audio_controls[UAC_RATE_CTRL].name = ctrl_name;
+
+ kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto snd_fail;
+ }
+
+ kctl->id.device = pcm->device;
+ kctl->id.subdevice = 0;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto snd_fail;
+ prm->snd_kctl_rate = kctl;
}
strscpy(card->driver, card_name, sizeof(card->driver));
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 8dfdae1721cd..9512b8fccfaa 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -10,6 +10,7 @@
#define __U_AUDIO_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
/*
* Same maximum frequency deviation on the slower side as in
@@ -40,16 +41,18 @@ struct uac_fu_params {
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
- int p_srate; /* rate in Hz */
+ int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int p_ssize; /* sample size */
struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
- int c_srate; /* rate in Hz */
+ int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int c_ssize; /* sample size */
struct uac_fu_params c_fu; /* Feature Unit parameters */
+ /* rates are dynamic, in uac_rtd_params */
+
int req_number; /* number of preallocated requests */
int fb_max; /* upper frequency drift feedback limit per-mil */
};
@@ -117,9 +120,16 @@ void u_audio_stop_capture(struct g_audio *g_audio);
int u_audio_start_playback(struct g_audio *g_audio);
void u_audio_stop_playback(struct g_audio *g_audio);
+int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val);
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
+int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val);
+int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
+
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
+void u_audio_suspend(struct g_audio *g_audio);
+
#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 589fae861141..f7a616760e31 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
@@ -30,10 +31,10 @@
struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
bool p_mute_present;
@@ -51,6 +52,8 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
+ char function_name[32];
+
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index e0c8e3513bfd..0510c9bad58d 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -14,13 +14,16 @@
#define U_UAC2_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
#define UAC2_DEF_PSSIZE 2
+#define UAC2_DEF_PHSBINT 0
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_CHSBINT 0
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
#define UAC2_DEF_MUTE_PRESENT 1
@@ -35,12 +38,14 @@
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
+ u8 p_hs_bint;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int c_sync;
+ u8 c_hs_bint;
bool p_mute_present;
bool p_volume_present;
@@ -58,6 +63,8 @@ struct f_uac2_opts {
int fb_max;
bool bound;
+ char function_name[32];
+
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/uac_common.h b/drivers/usb/gadget/function/uac_common.h
new file mode 100644
index 000000000000..3ecf89d6e814
--- /dev/null
+++ b/drivers/usb/gadget/function/uac_common.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ */
+
+#ifndef UAC_COMMON_H
+#define UAC_COMMON_H
+
+#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
+#endif
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index a748ed0842e8..76ea6decf7b6 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -22,91 +22,108 @@ USB_GADGET_COMPOSITE_OPTIONS();
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
-module_param(p_chmask, uint, S_IRUGO);
+module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC2_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
-module_param(p_ssize, uint, S_IRUGO);
+module_param(p_ssize, uint, 0444);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+/* Playback bInterval for HS/SS (1-4: fixed, 0: auto) */
+static u8 p_hs_bint = UAC2_DEF_PHSBINT;
+module_param(p_hs_bint, byte, 0444);
+MODULE_PARM_DESC(p_hs_bint,
+ "Playback bInterval for HS/SS (1-4: fixed, 0: auto)");
+
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
-module_param(c_chmask, uint, S_IRUGO);
+module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
-static int c_srate = UAC2_DEF_CSRATE;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
-module_param(c_ssize, uint, S_IRUGO);
+module_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+
+/* capture bInterval for HS/SS (1-4: fixed, 0: auto) */
+static u8 c_hs_bint = UAC2_DEF_CHSBINT;
+module_param(c_hs_bint, byte, 0444);
+MODULE_PARM_DESC(c_hs_bint,
+ "Capture bInterval for HS/SS (1-4: fixed, 0: auto)");
+
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
#include "u_uac1.h"
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC1_DEF_PCHMASK;
-module_param(p_chmask, uint, S_IRUGO);
+module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC1_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC1_DEF_PSSIZE;
-module_param(p_ssize, uint, S_IRUGO);
+module_param(p_ssize, uint, 0444);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC1_DEF_CCHMASK;
-module_param(c_chmask, uint, S_IRUGO);
+module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 48 KHz */
-static int c_srate = UAC1_DEF_CSRATE;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
-module_param(c_ssize, uint, S_IRUGO);
+module_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else /* CONFIG_GADGET_UAC1_LEGACY */
#include "u_uac1_legacy.h"
static char *fn_play = FILE_PCM_PLAYBACK;
-module_param(fn_play, charp, S_IRUGO);
+module_param(fn_play, charp, 0444);
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
static char *fn_cap = FILE_PCM_CAPTURE;
-module_param(fn_cap, charp, S_IRUGO);
+module_param(fn_cap, charp, 0444);
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
static char *fn_cntl = FILE_CONTROL;
-module_param(fn_cntl, charp, S_IRUGO);
+module_param(fn_cntl, charp, 0444);
MODULE_PARM_DESC(fn_cntl, "Control device file name");
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
-module_param(req_buf_size, int, S_IRUGO);
+module_param(req_buf_size, int, 0444);
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
static int req_count = UAC1_REQ_COUNT;
-module_param(req_count, int, S_IRUGO);
+module_param(req_count, int, 0444);
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
-module_param(audio_buf_size, int, S_IRUGO);
+module_param(audio_buf_size, int, 0444);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
#endif /* CONFIG_GADGET_UAC1_LEGACY */
#endif
@@ -237,9 +254,11 @@ static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
+ int i;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
+ int i;
#else
struct f_uac1_legacy_opts *uac1_opts;
#endif
@@ -263,20 +282,34 @@ static int audio_bind(struct usb_composite_dev *cdev)
#ifndef CONFIG_GADGET_UAC1
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
uac2_opts->p_chmask = p_chmask;
- uac2_opts->p_srate = p_srate;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac2_opts->p_srates[i] = p_srates[i];
+
uac2_opts->p_ssize = p_ssize;
+ uac2_opts->p_hs_bint = p_hs_bint;
uac2_opts->c_chmask = c_chmask;
- uac2_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac2_opts->c_srates[i] = c_srates[i];
+
uac2_opts->c_ssize = c_ssize;
+ uac2_opts->c_hs_bint = c_hs_bint;
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
uac1_opts->p_chmask = p_chmask;
- uac1_opts->p_srate = p_srate;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac1_opts->p_srates[i] = p_srates[i];
+
uac1_opts->p_ssize = p_ssize;
uac1_opts->c_chmask = c_chmask;
- uac1_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac1_opts->c_srates[i] = c_srates[i];
+
uac1_opts->c_ssize = c_ssize;
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 43f1b0d461c1..be76f891b9c5 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -32,9 +32,6 @@
#include <linux/workqueue.h>
/* XUSB_DEV registers */
-#define SPARAM 0x000
-#define SPARAM_ERSTMAX_MASK GENMASK(20, 16)
-#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK)
#define DB 0x004
#define DB_TARGET_MASK GENMASK(15, 8)
#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK)
@@ -275,8 +272,10 @@ BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff)
BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff)
BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff)
BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff)
-BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff)
+BUILD_EP_CONTEXT_RW(rsvd, rsvd[0], 24, 0x1)
BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1)
+BUILD_EP_CONTEXT_RW(splitxstate, rsvd[0], 26, 0x1)
+BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 27, 0x1f)
BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3)
BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff)
BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f)
@@ -1557,6 +1556,9 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
ep_reload(xudc, ep->index);
ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
+ ep_ctx_write_rsvd(ep->context, 0);
+ ep_ctx_write_partial_td(ep->context, 0);
+ ep_ctx_write_splitxstate(ep->context, 0);
ep_ctx_write_seq_num(ep->context, 0);
ep_reload(xudc, ep->index);
@@ -2812,7 +2814,10 @@ static void tegra_xudc_reset(struct tegra_xudc *xudc)
xudc->setup_seq_num = 0;
xudc->queued_setup_packet = false;
- ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num);
+ ep_ctx_write_rsvd(ep0->context, 0);
+ ep_ctx_write_partial_td(ep0->context, 0);
+ ep_ctx_write_splitxstate(ep0->context, 0);
+ ep_ctx_write_seq_num(ep0->context, 0);
deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]);
@@ -3295,11 +3300,6 @@ static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc)
unsigned int i;
u32 val;
- val = xudc_readl(xudc, SPARAM);
- val &= ~(SPARAM_ERSTMAX_MASK);
- val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS);
- xudc_writel(xudc, val, SPARAM);
-
for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE *
sizeof(*xudc->event_ring[i]));
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index e87cf3a00fa4..638f03b89739 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -21,6 +21,9 @@ static const char hcd_name[] = "ehci-pci";
/* defined here to avoid adding to pci_ids.h for single instance use */
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
+#define PCI_VENDOR_ID_ASPEED 0x1a03
+#define PCI_DEVICE_ID_ASPEED_EHCI 0x2603
+
/*-------------------------------------------------------------------------*/
#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939
static inline bool is_intel_quark_x1000(struct pci_dev *pdev)
@@ -222,6 +225,12 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
ehci->has_synopsys_hc_bug = 1;
}
break;
+ case PCI_VENDOR_ID_ASPEED:
+ if (pdev->device == PCI_DEVICE_ID_ASPEED_EHCI) {
+ ehci_info(ehci, "applying Aspeed HC workaround\n");
+ ehci->is_aspeed = 1;
+ }
+ break;
}
/* optional debug port, normally in the first BAR */
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 0f85aa9b2fb1..bd542b6fc46b 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1165,10 +1165,8 @@ static struct ehci_iso_sched *
iso_sched_alloc(unsigned packets, gfp_t mem_flags)
{
struct ehci_iso_sched *iso_sched;
- int size = sizeof(*iso_sched);
- size += packets * sizeof(struct ehci_iso_packet);
- iso_sched = kzalloc(size, mem_flags);
+ iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags);
if (likely(iso_sched != NULL))
INIT_LIST_HEAD(&iso_sched->td_list);
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 7af17c8e069b..c3fd375b4778 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -4014,10 +4014,8 @@ static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets,
gfp_t mem_flags)
{
struct fotg210_iso_sched *iso_sched;
- int size = sizeof(*iso_sched);
- size += packets * sizeof(struct fotg210_iso_packet);
- iso_sched = kzalloc(size, mem_flags);
+ iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags);
if (likely(iso_sched != NULL))
INIT_LIST_HEAD(&iso_sched->td_list);
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index edbfa82c6565..f3139ce7b0a9 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -248,7 +248,6 @@ create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
struct mu3h_sch_bw_info *bw_info;
struct mu3h_sch_tt *tt = NULL;
u32 len_bw_budget_table;
- size_t mem_size;
bw_info = get_bw_info(mtk, udev, ep);
if (!bw_info)
@@ -262,9 +261,9 @@ create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
else
len_bw_budget_table = 1;
- mem_size = sizeof(struct mu3h_sch_ep_info) +
- len_bw_budget_table * sizeof(u32);
- sch_ep = kzalloc(mem_size, GFP_KERNEL);
+ sch_ep = kzalloc(struct_size(sch_ep, bw_budget_table,
+ len_bw_budget_table),
+ GFP_KERNEL);
if (!sch_ep)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 91738af0ab14..96a0ff0bb11e 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -95,6 +95,19 @@
#define WC0_SSUSB0_CDEN BIT(6)
#define WC0_IS_SPM_EN BIT(1)
+/* mt8195 */
+#define PERI_WK_CTRL0_8195 0x04
+#define WC0_IS_P_95 BIT(30) /* polarity */
+#define WC0_IS_C_95(x) ((u32)(((x) & 0x7) << 27))
+#define WC0_IS_EN_P3_95 BIT(26)
+#define WC0_IS_EN_P2_95 BIT(25)
+#define WC0_IS_EN_P1_95 BIT(24)
+
+#define PERI_WK_CTRL1_8195 0x20
+#define WC1_IS_C_95(x) ((u32)(((x) & 0xf) << 28))
+#define WC1_IS_P_95 BIT(12)
+#define WC1_IS_EN_P0_95 BIT(6)
+
/* mt2712 etc */
#define PERI_SSUSB_SPM_CTRL 0x0
#define SSC_IP_SLEEP_EN BIT(4)
@@ -105,6 +118,10 @@ enum ssusb_uwk_vers {
SSUSB_UWK_V2,
SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */
SSUSB_UWK_V1_2, /* specific revision 1.2 */
+ SSUSB_UWK_V1_3, /* mt8195 IP0 */
+ SSUSB_UWK_V1_4, /* mt8195 IP1 */
+ SSUSB_UWK_V1_5, /* mt8195 IP2 */
+ SSUSB_UWK_V1_6, /* mt8195 IP3 */
};
/*
@@ -308,6 +325,26 @@ static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable)
msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN;
val = enable ? msk : 0;
break;
+ case SSUSB_UWK_V1_3:
+ reg = mtk->uwk_reg_base + PERI_WK_CTRL1_8195;
+ msk = WC1_IS_EN_P0_95 | WC1_IS_C_95(0xf) | WC1_IS_P_95;
+ val = enable ? (WC1_IS_EN_P0_95 | WC1_IS_C_95(0x1)) : 0;
+ break;
+ case SSUSB_UWK_V1_4:
+ reg = mtk->uwk_reg_base + PERI_WK_CTRL0_8195;
+ msk = WC0_IS_EN_P1_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
+ val = enable ? (WC0_IS_EN_P1_95 | WC0_IS_C_95(0x1)) : 0;
+ break;
+ case SSUSB_UWK_V1_5:
+ reg = mtk->uwk_reg_base + PERI_WK_CTRL0_8195;
+ msk = WC0_IS_EN_P2_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
+ val = enable ? (WC0_IS_EN_P2_95 | WC0_IS_C_95(0x1)) : 0;
+ break;
+ case SSUSB_UWK_V1_6:
+ reg = mtk->uwk_reg_base + PERI_WK_CTRL0_8195;
+ msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
+ val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0;
+ break;
case SSUSB_UWK_V2:
reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index dc570ce4e831..8094da34825e 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -226,20 +226,13 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (!sysdev)
sysdev = &pdev->dev;
- /* Try to set 64-bit DMA first */
if (WARN_ON(!sysdev->dma_mask))
/* Platform did not initialize dma_mask */
- ret = dma_coerce_mask_and_coherent(sysdev,
- DMA_BIT_MASK(64));
+ ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
else
ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
-
- /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
- if (ret) {
- ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 8f1144359012..c9407b86790c 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -137,6 +137,16 @@ config USB_APPLEDISPLAY
Say Y here if you want to control the backlight of Apple Cinema
Displays over USB. This driver provides a sysfs interface.
+config USB_QCOM_EUD
+ tristate "QCOM Embedded USB Debugger(EUD) Driver"
+ select USB_ROLE_SWITCH
+ help
+ This module enables support for Qualcomm Technologies, Inc.
+ Embedded USB Debugger (EUD). The EUD is a control peripheral
+ which reports VBUS attach/detach events and has USB-based
+ debug and trace capabilities. On selecting m, the module name
+ that is built is qcom_eud.ko
+
config APPLE_MFI_FASTCHARGE
tristate "Fast charge control for iOS devices"
select POWER_SUPPLY
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 5f4e598573ab..35bdb4b6c3b6 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
+obj-$(CONFIG_USB_QCOM_EUD) += qcom_eud.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
new file mode 100644
index 000000000000..f929bffdc5d1
--- /dev/null
+++ b/drivers/usb/misc/qcom_eud.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/usb/role.h>
+
+#define EUD_REG_INT1_EN_MASK 0x0024
+#define EUD_REG_INT_STATUS_1 0x0044
+#define EUD_REG_CTL_OUT_1 0x0074
+#define EUD_REG_VBUS_INT_CLR 0x0080
+#define EUD_REG_CSR_EUD_EN 0x1014
+#define EUD_REG_SW_ATTACH_DET 0x1018
+#define EUD_REG_EUD_EN2 0x0000
+
+#define EUD_ENABLE BIT(0)
+#define EUD_INT_PET_EUD BIT(0)
+#define EUD_INT_VBUS BIT(2)
+#define EUD_INT_SAFE_MODE BIT(4)
+#define EUD_INT_ALL (EUD_INT_VBUS | EUD_INT_SAFE_MODE)
+
+struct eud_chip {
+ struct device *dev;
+ struct usb_role_switch *role_sw;
+ void __iomem *base;
+ void __iomem *mode_mgr;
+ unsigned int int_status;
+ int irq;
+ bool enabled;
+ bool usb_attached;
+};
+
+static int enable_eud(struct eud_chip *priv)
+{
+ writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN);
+ writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
+ priv->base + EUD_REG_INT1_EN_MASK);
+ writel(1, priv->mode_mgr + EUD_REG_EUD_EN2);
+
+ return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE);
+}
+
+static void disable_eud(struct eud_chip *priv)
+{
+ writel(0, priv->base + EUD_REG_CSR_EUD_EN);
+ writel(0, priv->mode_mgr + EUD_REG_EUD_EN2);
+}
+
+static ssize_t enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct eud_chip *chip = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", chip->enabled);
+}
+
+static ssize_t enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct eud_chip *chip = dev_get_drvdata(dev);
+ bool enable;
+ int ret;
+
+ if (kstrtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable) {
+ ret = enable_eud(chip);
+ if (!ret)
+ chip->enabled = enable;
+ else
+ disable_eud(chip);
+ } else {
+ disable_eud(chip);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(enable);
+
+static struct attribute *eud_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(eud);
+
+static void usb_attach_detach(struct eud_chip *chip)
+{
+ u32 reg;
+
+ /* read ctl_out_1[4] to find USB attach or detach event */
+ reg = readl(chip->base + EUD_REG_CTL_OUT_1);
+ chip->usb_attached = reg & EUD_INT_SAFE_MODE;
+}
+
+static void pet_eud(struct eud_chip *chip)
+{
+ u32 reg;
+ int ret;
+
+ /* When the EUD_INT_PET_EUD in SW_ATTACH_DET is set, the cable has been
+ * disconnected and we need to detach the pet to check if EUD is in safe
+ * mode before attaching again.
+ */
+ reg = readl(chip->base + EUD_REG_SW_ATTACH_DET);
+ if (reg & EUD_INT_PET_EUD) {
+ /* Detach & Attach pet for EUD */
+ writel(0, chip->base + EUD_REG_SW_ATTACH_DET);
+ /* Delay to make sure detach pet is done before attach pet */
+ ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET,
+ reg, (reg == 0), 1, 100);
+ if (ret) {
+ dev_err(chip->dev, "Detach pet failed\n");
+ return;
+ }
+ }
+ /* Attach pet for EUD */
+ writel(EUD_INT_PET_EUD, chip->base + EUD_REG_SW_ATTACH_DET);
+}
+
+static irqreturn_t handle_eud_irq(int irq, void *data)
+{
+ struct eud_chip *chip = data;
+ u32 reg;
+
+ reg = readl(chip->base + EUD_REG_INT_STATUS_1);
+ switch (reg & EUD_INT_ALL) {
+ case EUD_INT_VBUS:
+ usb_attach_detach(chip);
+ return IRQ_WAKE_THREAD;
+ case EUD_INT_SAFE_MODE:
+ pet_eud(chip);
+ return IRQ_HANDLED;
+ default:
+ return IRQ_NONE;
+ }
+}
+
+static irqreturn_t handle_eud_irq_thread(int irq, void *data)
+{
+ struct eud_chip *chip = data;
+ int ret;
+
+ if (chip->usb_attached)
+ ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE);
+ else
+ ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST);
+ if (ret)
+ dev_err(chip->dev, "failed to set role switch\n");
+
+ /* set and clear vbus_int_clr[0] to clear interrupt */
+ writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR);
+ writel(0, chip->base + EUD_REG_VBUS_INT_CLR);
+
+ return IRQ_HANDLED;
+}
+
+static void eud_role_switch_release(void *data)
+{
+ struct eud_chip *chip = data;
+
+ usb_role_switch_put(chip->role_sw);
+}
+
+static int eud_probe(struct platform_device *pdev)
+{
+ struct eud_chip *chip;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+
+ ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
+ if (ret)
+ return dev_err_probe(chip->dev, ret,
+ "failed to add role switch release action\n");
+
+ chip->role_sw = usb_role_switch_get(&pdev->dev);
+ if (IS_ERR(chip->role_sw))
+ return dev_err_probe(chip->dev, PTR_ERR(chip->role_sw),
+ "failed to get role switch\n");
+
+ chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(chip->mode_mgr))
+ return PTR_ERR(chip->mode_mgr);
+
+ chip->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq,
+ handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip);
+ if (ret)
+ return dev_err_probe(chip->dev, ret, "failed to allocate irq\n");
+
+ enable_irq_wake(chip->irq);
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static int eud_remove(struct platform_device *pdev)
+{
+ struct eud_chip *chip = platform_get_drvdata(pdev);
+
+ if (chip->enabled)
+ disable_eud(chip);
+
+ device_init_wakeup(&pdev->dev, false);
+ disable_irq_wake(chip->irq);
+
+ return 0;
+}
+
+static const struct of_device_id eud_dt_match[] = {
+ { .compatible = "qcom,sc7280-eud" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, eud_dt_match);
+
+static struct platform_driver eud_driver = {
+ .probe = eud_probe,
+ .remove = eud_remove,
+ .driver = {
+ .name = "qcom_eud",
+ .dev_groups = eud_groups,
+ .of_match_table = eud_dt_match,
+ },
+};
+module_platform_driver(eud_driver);
+
+MODULE_DESCRIPTION("QTI EUD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index ab480f38523a..bc918ca61da8 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -52,6 +52,18 @@ source "drivers/usb/typec/ucsi/Kconfig"
source "drivers/usb/typec/tipd/Kconfig"
+config TYPEC_RT1719
+ tristate "Richtek RT1719 Sink Only Type-C controller driver"
+ depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y or M here if your system has Richtek RT1719 sink only
+ Type-C port controller driver.
+
+ If you choose to build this driver as a dynamically linked module, the
+ module will be called rt1719.ko
+
config TYPEC_HD3SS3220
tristate "TI HD3SS3220 Type-C DRP Port controller driver"
depends on I2C
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 57870a2bd787..441dd6c4cbef 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/
obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o
obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o
obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
+obj-$(CONFIG_TYPEC_RT1719) += rt1719.o
obj-$(CONFIG_TYPEC) += mux/
diff --git a/drivers/usb/typec/rt1719.c b/drivers/usb/typec/rt1719.c
new file mode 100644
index 000000000000..f1b698edd7eb
--- /dev/null
+++ b/drivers/usb/typec/rt1719.c
@@ -0,0 +1,961 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+
+#define RT1719_REG_TXCTRL1 0x03
+#define RT1719_REG_TXCTRL2 0x04
+#define RT1719_REG_POLICYINFO 0x0E
+#define RT1719_REG_SRCPDO1 0x11
+#define RT1719_REG_MASKS 0x2D
+#define RT1719_REG_EVENTS 0x33
+#define RT1719_REG_STATS 0x37
+#define RT1719_REG_PSELINFO 0x3C
+#define RT1719_REG_USBSETINFO 0x3E
+#define RT1719_REG_VENID 0x82
+
+#define RT1719_UNIQUE_PID 0x1719
+#define RT1719_REQDRSWAP_MASK BIT(7)
+#define RT1719_EVALMODE_MASK BIT(4)
+#define RT1719_REQSRCPDO_MASK GENMASK(2, 0)
+#define RT1719_TXSPDOREQ_MASK BIT(7)
+#define RT1719_INT_DRSW_ACCEPT BIT(23)
+#define RT1719_INT_RX_SRCCAP BIT(21)
+#define RT1719_INT_VBUS_DCT BIT(6)
+#define RT1719_INT_VBUS_PRESENT BIT(5)
+#define RT1719_INT_PE_SNK_RDY BIT(2)
+#define RT1719_CC1_STAT GENMASK(9, 8)
+#define RT1719_CC2_STAT GENMASK(11, 10)
+#define RT1719_POLARITY_MASK BIT(23)
+#define RT1719_DATAROLE_MASK BIT(22)
+#define RT1719_PDSPECREV_MASK GENMASK(21, 20)
+#define RT1719_SPDOSEL_MASK GENMASK(18, 16)
+#define RT1719_SPDONUM_MASK GENMASK(15, 13)
+#define RT1719_ATTACH_VBUS BIT(12)
+#define RT1719_ATTACH_DBG BIT(10)
+#define RT1719_ATTACH_SNK BIT(9)
+#define RT1719_ATTACHDEV_MASK (RT1719_ATTACH_VBUS | RT1719_ATTACH_DBG | \
+ RT1719_ATTACH_SNK)
+#define RT1719_PE_EXP_CONTRACT BIT(2)
+#define RT1719_PSEL_SUPPORT BIT(15)
+#define RT1719_TBLSEL_MASK BIT(6)
+#define RT1719_LATPSEL_MASK GENMASK(5, 0)
+#define RT1719_USBINFO_MASK GENMASK(1, 0)
+#define RT1719_USB_DFPUFP 3
+#define RT1719_MAX_SRCPDO 7
+
+enum {
+ SNK_PWR_OPEN = 0,
+ SNK_PWR_DEF,
+ SNK_PWR_1P5A,
+ SNK_PWR_3A
+};
+
+enum {
+ USBPD_SPECREV_1_0 = 0,
+ USBPD_SPECREV_2_0,
+ USBPD_SPECREV_3_0
+};
+
+enum rt1719_snkcap {
+ RT1719_SNKCAP_5V = 0,
+ RT1719_SNKCAP_9V,
+ RT1719_SNKCAP_12V,
+ RT1719_SNKCAP_15V,
+ RT1719_SNKCAP_20V,
+ RT1719_MAX_SNKCAP
+};
+
+struct rt1719_psel_cap {
+ u8 lomask;
+ u8 himask;
+ u32 milliwatt;
+ u32 milliamp;
+};
+
+struct rt1719_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct typec_port *port;
+ struct usb_role_switch *role_sw;
+ struct power_supply *psy;
+ struct typec_partner *partner;
+ struct power_supply_desc psy_desc;
+ struct usb_pd_identity partner_ident;
+ struct typec_partner_desc partner_desc;
+ struct completion req_completion;
+ enum power_supply_usb_type usb_type;
+ bool attached;
+ bool pd_capable;
+ bool drswap_support;
+ u32 voltage;
+ u32 req_voltage;
+ u32 max_current;
+ u32 op_current;
+ u32 spdos[RT1719_MAX_SRCPDO];
+ u16 snkcaps[RT1719_MAX_SNKCAP];
+ int spdo_num;
+ int spdo_sel;
+ u32 conn_info;
+ u16 conn_stat;
+};
+
+static const enum power_supply_usb_type rt1719_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_C,
+ POWER_SUPPLY_USB_TYPE_PD,
+ POWER_SUPPLY_USB_TYPE_PD_PPS
+};
+
+static const enum power_supply_property rt1719_psy_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW
+};
+
+static int rt1719_read16(struct rt1719_data *data, unsigned int reg, u16 *val)
+{
+ __le16 regval;
+ int ret;
+
+ ret = regmap_raw_read(data->regmap, reg, &regval, sizeof(regval));
+ if (ret)
+ return ret;
+
+ *val = le16_to_cpu(regval);
+ return 0;
+}
+
+static int rt1719_read32(struct rt1719_data *data, unsigned int reg, u32 *val)
+{
+ __le32 regval;
+ int ret;
+
+ ret = regmap_raw_read(data->regmap, reg, &regval, sizeof(regval));
+ if (ret)
+ return ret;
+
+ *val = le32_to_cpu(regval);
+ return 0;
+}
+
+static int rt1719_write32(struct rt1719_data *data, unsigned int reg, u32 val)
+{
+ __le32 regval = cpu_to_le32(val);
+
+ return regmap_raw_write(data->regmap, reg, &regval, sizeof(regval));
+}
+
+static enum typec_pwr_opmode rt1719_get_pwr_opmode(u32 conn, u16 stat)
+{
+ u16 cc1, cc2, cc_stat;
+
+ cc1 = FIELD_GET(RT1719_CC1_STAT, stat);
+ cc2 = FIELD_GET(RT1719_CC2_STAT, stat);
+
+ if (conn & RT1719_ATTACH_SNK) {
+ if (conn & RT1719_POLARITY_MASK)
+ cc_stat = cc2;
+ else
+ cc_stat = cc1;
+
+ switch (cc_stat) {
+ case SNK_PWR_3A:
+ return TYPEC_PWR_MODE_3_0A;
+ case SNK_PWR_1P5A:
+ return TYPEC_PWR_MODE_1_5A;
+ }
+ } else if (conn & RT1719_ATTACH_DBG) {
+ if ((cc1 == SNK_PWR_1P5A && cc2 == SNK_PWR_DEF) ||
+ (cc1 == SNK_PWR_DEF && cc2 == SNK_PWR_1P5A))
+ return TYPEC_PWR_MODE_1_5A;
+ else if ((cc1 == SNK_PWR_3A && cc2 == SNK_PWR_DEF) ||
+ (cc1 == SNK_PWR_DEF && cc2 == SNK_PWR_3A))
+ return TYPEC_PWR_MODE_3_0A;
+ }
+
+ return TYPEC_PWR_MODE_USB;
+}
+
+static enum typec_data_role rt1719_get_data_role(u32 conn)
+{
+ if (conn & RT1719_DATAROLE_MASK)
+ return TYPEC_HOST;
+ return TYPEC_DEVICE;
+}
+
+static void rt1719_set_data_role(struct rt1719_data *data,
+ enum typec_data_role data_role,
+ bool attached)
+{
+ enum usb_role usb_role = USB_ROLE_NONE;
+
+ if (attached) {
+ if (data_role == TYPEC_HOST)
+ usb_role = USB_ROLE_HOST;
+ else
+ usb_role = USB_ROLE_DEVICE;
+ }
+
+ usb_role_switch_set_role(data->role_sw, usb_role);
+ typec_set_data_role(data->port, data_role);
+}
+
+static void rt1719_update_data_role(struct rt1719_data *data)
+{
+ if (!data->attached)
+ return;
+
+ rt1719_set_data_role(data, rt1719_get_data_role(data->conn_info), true);
+}
+
+static void rt1719_register_partner(struct rt1719_data *data)
+{
+ u16 spec_rev = 0;
+
+ if (data->pd_capable) {
+ u32 rev;
+
+ rev = FIELD_GET(RT1719_PDSPECREV_MASK, data->conn_info);
+ switch (rev) {
+ case USBPD_SPECREV_3_0:
+ spec_rev = 0x0300;
+ break;
+ case USBPD_SPECREV_2_0:
+ spec_rev = 0x0200;
+ break;
+ default:
+ spec_rev = 0x0100;
+ break;
+ }
+ }
+
+ /* Just to prevent multiple times attach */
+ if (data->partner)
+ typec_unregister_partner(data->partner);
+
+ memset(&data->partner_ident, 0, sizeof(data->partner_ident));
+ data->partner_desc.usb_pd = data->pd_capable;
+ data->partner_desc.pd_revision = spec_rev;
+
+ if (data->conn_info & RT1719_ATTACH_DBG)
+ data->partner_desc.accessory = TYPEC_ACCESSORY_DEBUG;
+ else
+ data->partner_desc.accessory = TYPEC_ACCESSORY_NONE;
+
+ data->partner = typec_register_partner(data->port, &data->partner_desc);
+}
+
+static void rt1719_attach(struct rt1719_data *data)
+{
+ enum typec_pwr_opmode pwr_opmode;
+ enum typec_data_role data_role;
+ u32 volt = 5000, curr = 500;
+
+ if (!(data->conn_info & RT1719_ATTACHDEV_MASK))
+ return;
+
+ pwr_opmode = rt1719_get_pwr_opmode(data->conn_info, data->conn_stat);
+ data_role = rt1719_get_data_role(data->conn_info);
+
+ typec_set_pwr_opmode(data->port, pwr_opmode);
+ rt1719_set_data_role(data, data_role, true);
+
+ if (data->conn_info & RT1719_ATTACH_SNK)
+ rt1719_register_partner(data);
+
+ if (pwr_opmode == TYPEC_PWR_MODE_3_0A)
+ curr = 3000;
+ else if (pwr_opmode == TYPEC_PWR_MODE_1_5A)
+ curr = 1500;
+
+ data->voltage = volt * 1000;
+ data->max_current = data->op_current = curr * 1000;
+ data->attached = true;
+
+ power_supply_changed(data->psy);
+}
+
+static void rt1719_detach(struct rt1719_data *data)
+{
+ if (!data->attached || (data->conn_info & RT1719_ATTACHDEV_MASK))
+ return;
+
+ typec_unregister_partner(data->partner);
+ data->partner = NULL;
+
+ typec_set_pwr_opmode(data->port, TYPEC_PWR_MODE_USB);
+ rt1719_set_data_role(data, TYPEC_DEVICE, false);
+
+ memset32(data->spdos, 0, RT1719_MAX_SRCPDO);
+ data->spdo_num = 0;
+ data->voltage = data->max_current = data->op_current = 0;
+ data->attached = data->pd_capable = false;
+
+ data->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+ power_supply_changed(data->psy);
+}
+
+static void rt1719_update_operating_status(struct rt1719_data *data)
+{
+ enum power_supply_usb_type usb_type = POWER_SUPPLY_USB_TYPE_PD;
+ u32 voltage, max_current, op_current;
+ int i, snk_sel;
+
+ for (i = 0; i < data->spdo_num; i++) {
+ u32 pdo = data->spdos[i];
+ enum pd_pdo_type type = pdo_type(pdo);
+
+ if (type == PDO_TYPE_APDO) {
+ usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS;
+ break;
+ }
+ }
+
+ data->spdo_sel = FIELD_GET(RT1719_SPDOSEL_MASK, data->conn_info);
+ if (data->spdo_sel <= 0)
+ return;
+
+ data->usb_type = usb_type;
+
+ voltage = pdo_fixed_voltage(data->spdos[data->spdo_sel - 1]);
+ max_current = pdo_max_current(data->spdos[data->spdo_sel - 1]);
+
+ switch (voltage) {
+ case 5000:
+ snk_sel = RT1719_SNKCAP_5V;
+ break;
+ case 9000:
+ snk_sel = RT1719_SNKCAP_9V;
+ break;
+ case 12000:
+ snk_sel = RT1719_SNKCAP_12V;
+ break;
+ case 15000:
+ snk_sel = RT1719_SNKCAP_15V;
+ break;
+ case 20000:
+ snk_sel = RT1719_SNKCAP_20V;
+ break;
+ default:
+ return;
+ }
+
+ op_current = min(max_current, pdo_max_current(data->snkcaps[snk_sel]));
+
+ /* covert mV/mA to uV/uA */
+ data->voltage = voltage * 1000;
+ data->max_current = max_current * 1000;
+ data->op_current = op_current * 1000;
+
+ power_supply_changed(data->psy);
+}
+
+static void rt1719_update_pwr_opmode(struct rt1719_data *data)
+{
+ if (!data->attached)
+ return;
+
+ if (!data->pd_capable) {
+ data->pd_capable = true;
+
+ typec_set_pwr_opmode(data->port, TYPEC_PWR_MODE_PD);
+ rt1719_register_partner(data);
+ }
+
+ rt1719_update_operating_status(data);
+}
+
+static void rt1719_update_source_pdos(struct rt1719_data *data)
+{
+ int spdo_num = FIELD_GET(RT1719_SPDONUM_MASK, data->conn_info);
+ __le32 src_pdos[RT1719_MAX_SRCPDO] = { };
+ int i, ret;
+
+ if (!data->attached)
+ return;
+
+ ret = regmap_raw_read(data->regmap, RT1719_REG_SRCPDO1, src_pdos,
+ sizeof(__le32) * spdo_num);
+ if (ret)
+ return;
+
+ data->spdo_num = spdo_num;
+ for (i = 0; i < spdo_num; i++)
+ data->spdos[i] = le32_to_cpu(src_pdos[i]);
+}
+
+static int rt1719_dr_set(struct typec_port *port, enum typec_data_role role)
+{
+ struct rt1719_data *data = typec_get_drvdata(port);
+ enum typec_data_role cur_role;
+ int ret;
+
+ if (!data->attached || !data->pd_capable || !data->drswap_support)
+ return -EOPNOTSUPP;
+
+ if (data->spdo_num > 0 && !(data->spdos[0] & PDO_FIXED_DATA_SWAP))
+ return -EINVAL;
+
+ cur_role = rt1719_get_data_role(data->conn_info);
+ if (cur_role == role)
+ return 0;
+
+ ret = regmap_update_bits(data->regmap, RT1719_REG_TXCTRL1,
+ RT1719_REQDRSWAP_MASK, RT1719_REQDRSWAP_MASK);
+ if (ret)
+ return ret;
+
+ reinit_completion(&data->req_completion);
+ ret = wait_for_completion_timeout(&data->req_completion,
+ msecs_to_jiffies(400));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ cur_role = rt1719_get_data_role(data->conn_info);
+ if (cur_role != role)
+ return -EAGAIN;
+
+ rt1719_set_data_role(data, role, true);
+ return 0;
+}
+
+static const struct typec_operations rt1719_port_ops = {
+ .dr_set = rt1719_dr_set,
+};
+
+static int rt1719_usbpd_request_voltage(struct rt1719_data *data)
+{
+ u32 src_voltage;
+ int snk_sel, src_sel = -1;
+ int i, ret;
+
+ if (!data->attached || !data->pd_capable || data->spdo_sel <= 0)
+ return -EINVAL;
+
+ src_voltage = pdo_fixed_voltage(data->spdos[data->spdo_sel - 1]);
+ if (src_voltage == data->req_voltage)
+ return 0;
+
+ switch (data->req_voltage) {
+ case 5000:
+ snk_sel = RT1719_SNKCAP_5V;
+ break;
+ case 9000:
+ snk_sel = RT1719_SNKCAP_9V;
+ break;
+ case 12000:
+ snk_sel = RT1719_SNKCAP_12V;
+ break;
+ case 15000:
+ snk_sel = RT1719_SNKCAP_15V;
+ break;
+ case 20000:
+ snk_sel = RT1719_SNKCAP_20V;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!(data->snkcaps[snk_sel] & RT1719_PSEL_SUPPORT))
+ return -EINVAL;
+
+ for (i = 0; i < data->spdo_num; i++) {
+ enum pd_pdo_type type = pdo_type(data->spdos[i]);
+
+ if (type != PDO_TYPE_FIXED)
+ continue;
+
+ src_voltage = pdo_fixed_voltage(data->spdos[i]);
+ if (src_voltage == data->req_voltage) {
+ src_sel = i;
+ break;
+ }
+ }
+
+ if (src_sel == -1)
+ return -EOPNOTSUPP;
+
+ ret = regmap_update_bits(data->regmap, RT1719_REG_TXCTRL1,
+ RT1719_EVALMODE_MASK | RT1719_REQSRCPDO_MASK,
+ RT1719_EVALMODE_MASK | (src_sel + 1));
+ ret |= regmap_update_bits(data->regmap, RT1719_REG_TXCTRL2,
+ RT1719_TXSPDOREQ_MASK, RT1719_TXSPDOREQ_MASK);
+ if (ret)
+ return ret;
+
+ reinit_completion(&data->req_completion);
+ ret = wait_for_completion_timeout(&data->req_completion,
+ msecs_to_jiffies(400));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int rt1719_psy_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct rt1719_data *data = power_supply_get_drvdata(psy);
+
+ if (psp == POWER_SUPPLY_PROP_VOLTAGE_NOW) {
+ data->req_voltage = val->intval / 1000;
+ return rt1719_usbpd_request_voltage(data);
+ }
+
+ return -EINVAL;
+}
+
+static int rt1719_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct rt1719_data *data = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = data->attached ? 1 : 0;
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = data->usb_type;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = data->voltage;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = data->max_current;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = data->op_current;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rt1719_psy_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ if (psp == POWER_SUPPLY_PROP_VOLTAGE_NOW)
+ return 1;
+ return 0;
+}
+
+static int devm_rt1719_psy_register(struct rt1719_data *data)
+{
+ struct power_supply_config psy_cfg = { };
+ char *psy_name;
+
+ psy_cfg.fwnode = dev_fwnode(data->dev);
+ psy_cfg.drv_data = data;
+
+ psy_name = devm_kasprintf(data->dev, GFP_KERNEL, "rt1719-source-psy-%s",
+ dev_name(data->dev));
+ if (!psy_name)
+ return -ENOMEM;
+
+ data->psy_desc.name = psy_name;
+ data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+ data->psy_desc.usb_types = rt1719_psy_usb_types;
+ data->psy_desc.num_usb_types = ARRAY_SIZE(rt1719_psy_usb_types);
+ data->psy_desc.properties = rt1719_psy_properties;
+ data->psy_desc.num_properties = ARRAY_SIZE(rt1719_psy_properties);
+ data->psy_desc.get_property = rt1719_psy_get_property;
+ data->psy_desc.set_property = rt1719_psy_set_property;
+ data->psy_desc.property_is_writeable = rt1719_psy_property_is_writeable;
+
+ data->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+ data->psy = devm_power_supply_register(data->dev, &data->psy_desc,
+ &psy_cfg);
+
+ return PTR_ERR_OR_ZERO(data->psy);
+}
+
+static irqreturn_t rt1719_irq_handler(int irq, void *priv)
+{
+ struct rt1719_data *data = priv;
+ u32 events, conn_info;
+ u16 conn_stat;
+ int ret;
+
+ ret = rt1719_read32(data, RT1719_REG_EVENTS, &events);
+ ret |= rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
+ ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
+ if (ret)
+ return IRQ_NONE;
+
+ data->conn_info = conn_info;
+ data->conn_stat = conn_stat;
+
+ events &= (RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+ RT1719_INT_VBUS_PRESENT | RT1719_INT_VBUS_DCT |
+ RT1719_INT_PE_SNK_RDY);
+
+ if (events & RT1719_INT_DRSW_ACCEPT)
+ rt1719_update_data_role(data);
+
+ if (events & RT1719_INT_VBUS_PRESENT)
+ rt1719_attach(data);
+
+ if (events & RT1719_INT_VBUS_DCT)
+ rt1719_detach(data);
+
+ if (events & RT1719_INT_RX_SRCCAP)
+ rt1719_update_source_pdos(data);
+
+ if (events & RT1719_INT_PE_SNK_RDY) {
+ complete(&data->req_completion);
+ rt1719_update_pwr_opmode(data);
+ }
+
+ /* Write 1 to clear already handled events */
+ rt1719_write32(data, RT1719_REG_EVENTS, events);
+
+ return IRQ_HANDLED;
+}
+
+static int rt1719_irq_init(struct rt1719_data *data)
+{
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ u32 irq_enable;
+ int ret;
+
+ irq_enable = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+ RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
+ RT1719_INT_PE_SNK_RDY;
+
+ ret = rt1719_write32(data, RT1719_REG_MASKS, irq_enable);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to config irq enable\n");
+ return ret;
+ }
+
+ return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt1719_irq_handler, IRQF_ONESHOT,
+ dev_name(&i2c->dev), data);
+}
+
+static int rt1719_init_attach_state(struct rt1719_data *data)
+{
+ u32 conn_info, irq_clear;
+ u16 conn_stat;
+ int ret;
+
+ irq_clear = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+ RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
+ RT1719_INT_PE_SNK_RDY;
+
+ ret = rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
+ ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
+ ret |= rt1719_write32(data, RT1719_REG_EVENTS, irq_clear);
+ if (ret)
+ return ret;
+
+ data->conn_info = conn_info;
+ data->conn_stat = conn_stat;
+
+ if (conn_info & RT1719_ATTACHDEV_MASK)
+ rt1719_attach(data);
+
+ if (conn_info & RT1719_PE_EXP_CONTRACT) {
+ rt1719_update_source_pdos(data);
+ rt1719_update_pwr_opmode(data);
+ }
+
+ return 0;
+}
+
+#define RT1719_PSEL_CAPINFO(_lomask, _milliwatt, _himask, _milliamp) { \
+ .lomask = _lomask, \
+ .milliwatt = _milliwatt, \
+ .himask = _himask, \
+ .milliamp = _milliamp, \
+}
+
+static const struct rt1719_psel_cap rt1719_psel_caps[] = {
+ RT1719_PSEL_CAPINFO(0x18, 75000, 0x10, 5000),
+ RT1719_PSEL_CAPINFO(0x18, 60000, 0x10, 4500),
+ RT1719_PSEL_CAPINFO(0x18, 45000, 0x10, 4000),
+ RT1719_PSEL_CAPINFO(0x18, 30000, 0x10, 3500),
+ RT1719_PSEL_CAPINFO(0x18, 25000, 0x10, 3000),
+ RT1719_PSEL_CAPINFO(0x18, 20000, 0x10, 2500),
+ RT1719_PSEL_CAPINFO(0x18, 15000, 0x10, 2000),
+ RT1719_PSEL_CAPINFO(0x18, 10000, 0x10, 1000),
+ RT1719_PSEL_CAPINFO(0x1C, 60000, 0x1F, 5000),
+ RT1719_PSEL_CAPINFO(0x1C, 45000, 0x1F, 4500),
+ RT1719_PSEL_CAPINFO(0x1C, 30000, 0x1F, 4000),
+ RT1719_PSEL_CAPINFO(0x1C, 24000, 0x1F, 3500),
+ RT1719_PSEL_CAPINFO(0x1C, 15000, 0x1F, 3000),
+ RT1719_PSEL_CAPINFO(0x1C, 10000, 0x1F, 2500),
+ RT1719_PSEL_CAPINFO(0x0C, 60000, 0x1F, 2000),
+ RT1719_PSEL_CAPINFO(0x0C, 45000, 0x1F, 1000),
+ RT1719_PSEL_CAPINFO(0x0C, 36000, 0x08, 5000),
+ RT1719_PSEL_CAPINFO(0x0C, 30000, 0x08, 4500),
+ RT1719_PSEL_CAPINFO(0x0C, 24000, 0x08, 4000),
+ RT1719_PSEL_CAPINFO(0x0C, 15000, 0x08, 3500),
+ RT1719_PSEL_CAPINFO(0x0C, 10000, 0x08, 3000),
+ RT1719_PSEL_CAPINFO(0x1E, 45000, 0x08, 2500),
+ RT1719_PSEL_CAPINFO(0x1E, 36000, 0x08, 2000),
+ RT1719_PSEL_CAPINFO(0x1E, 27000, 0x08, 1500),
+ RT1719_PSEL_CAPINFO(0x1E, 20000, 0x08, 1000),
+ RT1719_PSEL_CAPINFO(0x1E, 15000, 0x0F, 5000),
+ RT1719_PSEL_CAPINFO(0x1E, 9000, 0x0F, 4500),
+ RT1719_PSEL_CAPINFO(0x0E, 45000, 0x0F, 4000),
+ RT1719_PSEL_CAPINFO(0x0E, 36000, 0x0F, 3500),
+ RT1719_PSEL_CAPINFO(0x0E, 27000, 0x0F, 3000),
+ RT1719_PSEL_CAPINFO(0x0E, 20000, 0x0F, 2500),
+ RT1719_PSEL_CAPINFO(0x0E, 15000, 0x0F, 2000),
+ RT1719_PSEL_CAPINFO(0x0E, 9000, 0x0F, 1500),
+ RT1719_PSEL_CAPINFO(0x06, 45000, 0x0F, 1000),
+ RT1719_PSEL_CAPINFO(0x06, 36000, 0x0F, 500),
+ RT1719_PSEL_CAPINFO(0x06, 27000, 0x04, 5000),
+ RT1719_PSEL_CAPINFO(0x06, 24000, 0x04, 4500),
+ RT1719_PSEL_CAPINFO(0x06, 18000, 0x04, 4000),
+ RT1719_PSEL_CAPINFO(0x06, 12000, 0x04, 3500),
+ RT1719_PSEL_CAPINFO(0x06, 9000, 0x04, 3000),
+ RT1719_PSEL_CAPINFO(0x1F, 25000, 0x04, 2500),
+ RT1719_PSEL_CAPINFO(0x1F, 20000, 0x04, 2000),
+ RT1719_PSEL_CAPINFO(0x1F, 15000, 0x04, 1500),
+ RT1719_PSEL_CAPINFO(0x1F, 10000, 0x04, 1000),
+ RT1719_PSEL_CAPINFO(0x1F, 7500, 0x07, 5000),
+ RT1719_PSEL_CAPINFO(0x0F, 25000, 0x07, 4500),
+ RT1719_PSEL_CAPINFO(0x0F, 20000, 0x07, 4000),
+ RT1719_PSEL_CAPINFO(0x0F, 15000, 0x07, 3500),
+ RT1719_PSEL_CAPINFO(0x0F, 10000, 0x07, 3000),
+ RT1719_PSEL_CAPINFO(0x0F, 7500, 0x07, 2500),
+ RT1719_PSEL_CAPINFO(0x07, 25000, 0x07, 2000),
+ RT1719_PSEL_CAPINFO(0x07, 20000, 0x07, 1500),
+ RT1719_PSEL_CAPINFO(0x07, 15000, 0x07, 1000),
+ RT1719_PSEL_CAPINFO(0x07, 10000, 0x07, 500),
+ RT1719_PSEL_CAPINFO(0x07, 7500, 0x03, 5000),
+ RT1719_PSEL_CAPINFO(0x03, 25000, 0x03, 4500),
+ RT1719_PSEL_CAPINFO(0x03, 20000, 0x03, 4000),
+ RT1719_PSEL_CAPINFO(0x03, 15000, 0x03, 3500),
+ RT1719_PSEL_CAPINFO(0x03, 10000, 0x03, 3000),
+ RT1719_PSEL_CAPINFO(0x03, 7500, 0x03, 2500),
+ RT1719_PSEL_CAPINFO(0x01, 15000, 0x03, 2000),
+ RT1719_PSEL_CAPINFO(0x01, 10000, 0x03, 1500),
+ RT1719_PSEL_CAPINFO(0x01, 7500, 0x03, 1000),
+ RT1719_PSEL_CAPINFO(0x01, 2500, 0x03, 500)
+};
+
+static u16 rt1719_gen_snkcap_by_current(const struct rt1719_psel_cap *psel_cap,
+ enum rt1719_snkcap capsel)
+{
+ u16 cap = RT1719_PSEL_SUPPORT;
+
+ if (!(psel_cap->himask & BIT(capsel)))
+ return 0;
+
+ cap |= psel_cap->milliamp / 10;
+ return cap;
+}
+
+static u16 rt1719_gen_snkcap_by_watt(const struct rt1719_psel_cap *psel_cap,
+ enum rt1719_snkcap capsel)
+{
+ u32 volt_div[RT1719_MAX_SNKCAP] = { 5, 9, 12, 15, 20 };
+ u16 cap = RT1719_PSEL_SUPPORT;
+
+ if (!(psel_cap->lomask & BIT(capsel)))
+ return 0;
+
+ cap |= min(psel_cap->milliwatt / volt_div[capsel], (u32)5000) / 10;
+ return cap;
+}
+
+static u16 rt1719_gen_snkcap(unsigned int pselinfo, enum rt1719_snkcap capsel)
+{
+ int psel = FIELD_GET(RT1719_LATPSEL_MASK, pselinfo);
+ const struct rt1719_psel_cap *psel_cap;
+ bool by_current = false;
+
+ if (pselinfo & RT1719_TBLSEL_MASK)
+ by_current = true;
+
+ psel_cap = rt1719_psel_caps + psel;
+ if (by_current)
+ return rt1719_gen_snkcap_by_current(psel_cap, capsel);
+
+ return rt1719_gen_snkcap_by_watt(psel_cap, capsel);
+}
+
+static int rt1719_get_caps(struct rt1719_data *data)
+{
+ unsigned int pselinfo, usbinfo;
+ int i, ret;
+
+ ret = regmap_read(data->regmap, RT1719_REG_PSELINFO, &pselinfo);
+ ret |= regmap_read(data->regmap, RT1719_REG_USBSETINFO, &usbinfo);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < RT1719_MAX_SNKCAP; i++)
+ data->snkcaps[i] = rt1719_gen_snkcap(pselinfo, i);
+
+ usbinfo = FIELD_GET(RT1719_USBINFO_MASK, usbinfo);
+ if (usbinfo == RT1719_USB_DFPUFP)
+ data->drswap_support = true;
+
+ return 0;
+}
+
+static int rt1719_check_exist(struct rt1719_data *data)
+{
+ u16 pid;
+ int ret;
+
+ ret = rt1719_read16(data, RT1719_REG_VENID, &pid);
+ if (ret)
+ return ret;
+
+ if (pid != RT1719_UNIQUE_PID) {
+ dev_err(data->dev, "Incorrect PID 0x%04x\n", pid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config rt1719_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+};
+
+static int rt1719_probe(struct i2c_client *i2c)
+{
+ struct rt1719_data *data;
+ struct fwnode_handle *fwnode;
+ struct typec_capability typec_cap = { };
+ int ret;
+
+ data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &i2c->dev;
+ init_completion(&data->req_completion);
+
+ data->regmap = devm_regmap_init_i2c(i2c, &rt1719_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap (%d)\n", ret);
+ return ret;
+ }
+
+ ret = rt1719_check_exist(data);
+ if (ret)
+ return ret;
+
+ ret = rt1719_get_caps(data);
+ if (ret)
+ return ret;
+
+ fwnode = device_get_named_child_node(&i2c->dev, "connector");
+ if (!fwnode)
+ return -ENODEV;
+
+ data->role_sw = fwnode_usb_role_switch_get(fwnode);
+ if (IS_ERR(data->role_sw)) {
+ ret = PTR_ERR(data->role_sw);
+ dev_err(&i2c->dev, "Failed to get usb role switch (%d)\n", ret);
+ goto err_fwnode_put;
+ }
+
+ ret = devm_rt1719_psy_register(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to register psy (%d)\n", ret);
+ goto err_role_put;
+ }
+
+ typec_cap.revision = USB_TYPEC_REV_1_2;
+ typec_cap.pd_revision = 0x300; /* USB-PD spec release 3.0 */
+ typec_cap.type = TYPEC_PORT_SNK;
+ typec_cap.data = TYPEC_PORT_DRD;
+ typec_cap.ops = &rt1719_port_ops;
+ typec_cap.fwnode = fwnode;
+ typec_cap.driver_data = data;
+ typec_cap.accessory[0] = TYPEC_ACCESSORY_DEBUG;
+
+ data->partner_desc.identity = &data->partner_ident;
+
+ data->port = typec_register_port(&i2c->dev, &typec_cap);
+ if (IS_ERR(data->port)) {
+ ret = PTR_ERR(data->port);
+ dev_err(&i2c->dev, "Failed to register typec port (%d)\n", ret);
+ goto err_role_put;
+ }
+
+ ret = rt1719_init_attach_state(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to init attach state (%d)\n", ret);
+ goto err_role_put;
+ }
+
+ ret = rt1719_irq_init(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to init irq\n");
+ goto err_role_put;
+ }
+
+ fwnode_handle_put(fwnode);
+
+ i2c_set_clientdata(i2c, data);
+
+ return 0;
+
+err_role_put:
+ usb_role_switch_put(data->role_sw);
+err_fwnode_put:
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static int rt1719_remove(struct i2c_client *i2c)
+{
+ struct rt1719_data *data = i2c_get_clientdata(i2c);
+
+ typec_unregister_port(data->port);
+ usb_role_switch_put(data->role_sw);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused rt1719_device_table[] = {
+ { .compatible = "richtek,rt1719", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1719_device_table);
+
+static struct i2c_driver rt1719_driver = {
+ .driver = {
+ .name = "rt1719",
+ .of_match_table = rt1719_device_table,
+ },
+ .probe_new = rt1719_probe,
+ .remove = rt1719_remove,
+};
+module_i2c_driver(rt1719_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT1719 Sink Only USBPD Controller Driver");
+MODULE_LICENSE("GPL v2");