kernel-brax3-ubuntu-touch/drivers/misc/mediatek/usb/usb_xhci/quirks.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

178 lines
4.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* MTK xhci quirk driver
*
* Copyright (c) 2022 MediaTek Inc.
* Author: Denis Hsu <denis.hsu@mediatek.com>
*/
#include <linux/usb/quirks.h>
#include <trace/hooks/audio_usboffload.h>
#include "quirks.h"
#include "xhci-mtk.h"
#include "xhci-trace.h"
struct usb_audio_quirk_flags_table {
u32 id;
u32 flags;
};
#define DEVICE_FLG(vid, pid, _flags) \
{ .id = USB_ID(vid, pid), .flags = (_flags) }
#define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
/* quirk list in usbcore */
static const struct usb_device_id mtk_usb_quirk_list[] = {
{USB_DEVICE(0x12d1, 0x3a07), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP|USB_QUIRK_RESET},
{ } /* terminating entry must be last */
};
/* quirk list in /sound/usb */
static const struct usb_audio_quirk_flags_table mtk_snd_quirk_flags_table[] = {
DEVICE_FLG(0x2d99, 0xa026,
QUIRK_FLAG_CTL_MSG_DELAY),
DEVICE_FLG(0x12d1, 0x3a07,
QUIRK_FLAG_CTL_MSG_DELAY),
{} /* terminator */
};
static int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
return 0;
/* No need to test id->bcdDevice_lo != 0, since 0 is never
greater than any unsigned number. */
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
(id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
(id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
(id->bDeviceClass != dev->descriptor.bDeviceClass))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
(id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
return 0;
return 1;
}
static u32 usb_detect_static_quirks(struct usb_device *udev,
const struct usb_device_id *id)
{
u32 quirks = 0;
for (; id->match_flags; id++) {
if (!usb_match_device(udev, id))
continue;
quirks |= (u32)(id->driver_info);
dev_info(&udev->dev,
"Set usbcore quirk_flags 0x%x for device %04x:%04x\n",
(u32)id->driver_info, id->idVendor,
id->idProduct);
}
return quirks;
}
static void snd_usb_init_quirk_flags(struct snd_usb_audio *chip)
{
const struct usb_audio_quirk_flags_table *p;
for (p = mtk_snd_quirk_flags_table; p->id; p++) {
if (chip->usb_id == p->id ||
(!USB_ID_PRODUCT(p->id) &&
USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
dev_info(&chip->dev->dev,
"Set audio quirk_flags 0x%x for device %04x:%04x\n",
p->flags, USB_ID_VENDOR(chip->usb_id),
USB_ID_PRODUCT(chip->usb_id));
chip->quirk_flags |= p->flags;
return;
}
}
}
/* update mtk usbcore quirk */
void xhci_mtk_apply_quirk(struct usb_device *udev)
{
if (!udev)
return;
udev->quirks = usb_detect_static_quirks(udev, mtk_usb_quirk_list);
}
/* update mtk usb audio quirk */
void xhci_mtk_sound_usb_connect(void *unused, struct usb_interface *intf, struct snd_usb_audio *chip)
{
if (!chip)
return;
snd_usb_init_quirk_flags(chip);
}
static void xhci_trace_ep0_urb(void *data, struct urb *urb)
{
struct device *hcd_dev = (struct device *)data;
struct usb_ctrlrequest *ctrl = NULL;
struct usb_host_config *config = NULL;
struct usb_interface_descriptor *intf_desc = NULL;
int config_num, i;
if (!urb || !urb->setup_packet || !urb->dev) {
//pri liuyong, modify for file restore, 20240812 start
//dev_dbg(hcd_dev, "%s urb/setup pkt/device can't be NULL\n", __func__);
//pri liuyong, modify for file restore, 20240812 end
return;
}
ctrl = (struct usb_ctrlrequest *)urb->setup_packet;
if (ctrl->bRequest != USB_REQ_SET_INTERFACE || ctrl->wValue == 0) {
dev_dbg(hcd_dev, "%s it's not ep0 transfer request\n", __func__);
return;
}
config = urb->dev->config;
if (!config)
return;
config_num = urb->dev->descriptor.bNumConfigurations;
for (i = 0; i < config_num; i++, config++) {
if (config && config->desc.bNumInterfaces > 0)
intf_desc = &config->intf_cache[0]->altsetting->desc;
if (intf_desc && intf_desc->bInterfaceClass == USB_CLASS_AUDIO) {
dev_dbg(hcd_dev, "delay 5ms for UAC device\n");
mdelay(5);
return;
}
}
}
void xhci_mtk_trace_init(struct device *dev)
{
WARN_ON(register_trace_xhci_urb_enqueue_(xhci_trace_ep0_urb, dev));
WARN_ON(register_trace_android_vh_audio_usb_offload_connect(xhci_mtk_sound_usb_connect, NULL));
}
void xhci_mtk_trace_deinit(struct device *dev)
{
WARN_ON(unregister_trace_xhci_urb_enqueue_(xhci_trace_ep0_urb, dev));
unregister_trace_android_vh_audio_usb_offload_connect(xhci_mtk_sound_usb_connect, NULL);
}