kernel_samsung_a34x-permissive/drivers/misc/mediatek/usb_offload/usb_offload.c

2165 lines
56 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* MTK USB Offload Driver
* *
* Copyright (c) 2022 MediaTek Inc.
* Author: Jeremy Chou <jeremy.chou@mediatek.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/genalloc.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
#include <linux/uaccess.h>
#include <sound/pcm.h>
#include <sound/core.h>
#include <sound/asound.h>
#include <linux/iommu.h>
#include <linux/dma-mapping.h>
#if IS_ENABLED(CONFIG_SND_USB_AUDIO)
#include "usbaudio.h"
#include "card.h"
#include "helper.h"
#include "pcm.h"
#include "power.h"
#endif
#if IS_ENABLED(CONFIG_USB_XHCI_MTK)
#include "xhci.h"
#include "xhci-mtk.h"
#endif
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
#include <adsp_helper.h>
#include "audio_messenger_ipi.h"
#include "audio_task.h"
#include "audio_controller_msg_id.h"
#endif
#include "usb_offload.h"
#include "audio_task_usb_msg_id.h"
static unsigned int usb_offload_log;
module_param(usb_offload_log, uint, 0644);
MODULE_PARM_DESC(usb_offload_log, "Enable/Disable USB Offload log");
#define USB_OFFLOAD_MEM_DBG(fmt, args...) do { \
if (usb_offload_log > 1) \
pr_info("UD, %s(%d) " fmt, __func__, __LINE__, ## args); \
} while (0)
#define USB_OFFLOAD_INFO(fmt, args...) do { \
if (1) \
pr_info("UO, %s(%d) " fmt, __func__, __LINE__, ## args); \
} while (0)
#define USB_OFFLOAD_PROBE(fmt, args...) do { \
if (1) \
pr_info("UD, %s(%d) " fmt, __func__, __LINE__, ## args); \
} while (0)
#define USB_OFFLOAD_ERR(fmt, args...) do { \
if (1) \
pr_info("UD, %s(%d) " fmt, __func__, __LINE__, ## args); \
} while (0)
struct usb_offload_buffer *buf_dcbaa;
struct usb_offload_buffer *buf_ctx;
struct usb_offload_buffer *buf_seg;
static struct usb_audio_dev uadev[SNDRV_CARDS];
static struct usb_offload_dev *uodev;
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static void uaudio_disconnect_cb(struct snd_usb_audio *chip);
static void adsp_ee_recovery(void)
{
u32 temp, irq_pending;
u64 temp_64;
if (!uodev->xhci)
return;
USB_OFFLOAD_INFO("ADSP EE ++ op:0x%08x, iman:0x%08X, erdp:0x%08X\n",
readl(&uodev->xhci->op_regs->status),
readl(&uodev->xhci->run_regs->ir_set[1].irq_pending),
xhci_read_64(uodev->xhci, &uodev->xhci->ir_set[1].erst_dequeue));
USB_OFFLOAD_INFO("// Disabling event ring interrupts\n");
temp = readl(&uodev->xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &uodev->xhci->op_regs->status);
temp = readl(&uodev->xhci->ir_set[1].irq_pending);
writel(ER_IRQ_DISABLE(temp), &uodev->xhci->ir_set[1].irq_pending);
irq_pending = readl(&uodev->xhci->run_regs->ir_set[1].irq_pending);
irq_pending |= IMAN_IP;
writel(irq_pending, &uodev->xhci->run_regs->ir_set[1].irq_pending);
temp_64 = xhci_read_64(uodev->xhci, &uodev->xhci->ir_set[1].erst_dequeue);
/* Clear the event handler busy flag (RW1C) */
temp_64 |= ERST_EHB;
xhci_write_64(uodev->xhci, temp_64, &uodev->xhci->ir_set[1].erst_dequeue);
uodev->adsp_exception = false;
USB_OFFLOAD_INFO("ADSP EE -- op:0x%08x, iman:0x%08X, erdp:0x%08X\n",
readl(&uodev->xhci->op_regs->status),
readl(&uodev->xhci->run_regs->ir_set[1].irq_pending),
xhci_read_64(uodev->xhci, &uodev->xhci->ir_set[1].erst_dequeue));
}
#ifdef CFG_RECOVERY_SUPPORT
static int usb_offload_event_receive(struct notifier_block *this, unsigned long event,
void *ptr)
{
int ret = 0;
switch (event) {
case ADSP_EVENT_STOP:
pr_info("%s event[%lu]\n", __func__, event);
uodev->adsp_exception = true;
uodev->adsp_ready = false;
if (uodev->connected)
adsp_ee_recovery();
break;
case ADSP_EVENT_READY: {
pr_info("%s event[%lu]\n", __func__, event);
uodev->adsp_ready = true;
break;
}
default:
pr_info("%s event[%lu]\n", __func__, event);
}
return ret;
}
static struct notifier_block adsp_usb_offload_notifier = {
.notifier_call = usb_offload_event_receive,
.priority = PRIMARY_FEATURE_PRI,
};
#endif
/*static*/ void sound_usb_connect(struct usb_interface *intf, struct snd_usb_audio *chip)
{
struct device_node *node_xhci_host;
struct platform_device *pdev_xhci_host = NULL;
struct xhci_hcd_mtk *mtk;
struct xhci_hcd *xhci;
USB_OFFLOAD_INFO("index=%d\n", chip->index);
if (chip->index >= 0)
usb_chip[chip->index] = chip;
uodev->is_streaming = false;
uodev->tx_streaming = false;
uodev->rx_streaming = false;
uodev->adsp_inited = false;
uodev->connected = true;
uodev->opened = false;
uodev->adsp_exception = false;
node_xhci_host = of_parse_phandle(uodev->dev->of_node, "xhci_host", 0);
if (node_xhci_host) {
pdev_xhci_host = of_find_device_by_node(node_xhci_host);
if (!pdev_xhci_host) {
USB_OFFLOAD_ERR("no device found by node!\n");
return;
}
of_node_put(node_xhci_host);
mtk = platform_get_drvdata(pdev_xhci_host);
if (!mtk) {
USB_OFFLOAD_ERR("no drvdata set!\n");
return;
}
xhci = hcd_to_xhci(mtk->hcd);
uodev->xhci = xhci;
} else {
USB_OFFLOAD_ERR("No 'xhci_host' node, NOT SUPPORT USB Offload!\n");
uodev->xhci = NULL;
return;
}
}
EXPORT_SYMBOL_GPL(sound_usb_connect);
/*static*/ void sound_usb_disconnect(struct usb_interface *intf)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
unsigned int card_num;
USB_OFFLOAD_INFO("\n");
uodev->is_streaming = false;
uodev->tx_streaming = false;
uodev->rx_streaming = false;
uodev->adsp_inited = false;
uodev->connected = false;
uodev->opened = false;
uodev->adsp_exception = false;
if (chip == USB_AUDIO_IFACE_UNUSED)
return;
card_num = chip->card->number;
USB_OFFLOAD_INFO("index=%d num_interfaces=%d, card_num=%d\n",
chip->index, chip->num_interfaces, card_num);
uaudio_disconnect_cb(chip);
if (chip->num_interfaces < 1)
if (chip->index >= 0)
usb_chip[chip->index] = NULL;
}
EXPORT_SYMBOL_GPL(sound_usb_disconnect);
static bool is_support_format(snd_pcm_format_t fmt)
{
switch (fmt) {
case SNDRV_PCM_FORMAT_S8:
case SNDRV_PCM_FORMAT_U8:
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S16_BE:
case SNDRV_PCM_FORMAT_U16_LE:
case SNDRV_PCM_FORMAT_U16_BE:
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S24_BE:
case SNDRV_PCM_FORMAT_U24_LE:
case SNDRV_PCM_FORMAT_U24_BE:
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_3BE:
case SNDRV_PCM_FORMAT_U24_3LE:
case SNDRV_PCM_FORMAT_U24_3BE:
case SNDRV_PCM_FORMAT_S32_LE:
case SNDRV_PCM_FORMAT_S32_BE:
case SNDRV_PCM_FORMAT_U32_LE:
case SNDRV_PCM_FORMAT_U32_BE:
return true;
default:
return false;
}
}
static enum usb_audio_device_speed
get_speed_info(enum usb_device_speed udev_speed)
{
switch (udev_speed) {
case USB_SPEED_LOW:
return USB_AUDIO_DEVICE_SPEED_LOW;
case USB_SPEED_FULL:
return USB_AUDIO_DEVICE_SPEED_FULL;
case USB_SPEED_HIGH:
return USB_AUDIO_DEVICE_SPEED_HIGH;
case USB_SPEED_SUPER:
return USB_AUDIO_DEVICE_SPEED_SUPER;
case USB_SPEED_SUPER_PLUS:
return USB_AUDIO_DEVICE_SPEED_SUPER_PLUS;
default:
USB_OFFLOAD_INFO("udev speed %d\n", udev_speed);
return USB_AUDIO_DEVICE_SPEED_INVALID;
}
}
static bool is_uainfo_valid(struct usb_audio_stream_info *uainfo)
{
if (uainfo == NULL) {
USB_OFFLOAD_ERR("uainfo is NULL\n");
return false;
}
if (uainfo->enable > 1) {
USB_OFFLOAD_ERR("uainfo->enable invalid (%d)\n", uainfo->enable);
return false;
}
if (uainfo->bit_rate > 768000) {
USB_OFFLOAD_ERR("uainfo->bit_rate invalid (%d)\n", uainfo->bit_rate);
return false;
}
if (uainfo->bit_depth > 32) {
USB_OFFLOAD_ERR("uainfo->bit_depth invalid (%d)\n", uainfo->bit_depth);
return false;
}
if (uainfo->direction > 1) {
USB_OFFLOAD_ERR("uainfo->direction invalid (%d)\n", uainfo->direction);
return false;
}
return true;
}
static void dump_uainfo(struct usb_audio_stream_info *uainfo)
{
USB_OFFLOAD_INFO("uainfo->enable: %d\n"
"uainfo->bit_rate: %d\n"
"uainfo->number_of_ch: %d\n"
"uainfo->bit_depth: %d\n"
"uainfo->direction: %d\n"
"uainfo->pcm_card_num: %d\n"
"uainfo->pcm_dev_num: %d\n"
"uainfo->xhc_irq_period_ms: %d\n"
"uainfo->xhc_urb_num: %d\n"
"uainfo->dram_size: %d\n"
"uainfo->dram_cnt: %d\n"
"uainfo->start_thld: %d\n"
"uainfo->stop_thld: %d\n"
"uainfo->pcm_size: %d\n"
"uainfo->service_interval: %d\n"
"uainfo->service_interval_valid: %d\n",
uainfo->enable,
uainfo->bit_rate,
uainfo->number_of_ch,
uainfo->bit_depth,
uainfo->direction,
uainfo->pcm_card_num,
uainfo->pcm_dev_num,
uainfo->xhc_irq_period_ms,
uainfo->xhc_urb_num,
uainfo->dram_size,
uainfo->dram_cnt,
uainfo->start_thld,
uainfo->stop_thld,
uainfo->pcm_size,
uainfo->service_interval,
uainfo->service_interval_valid);
}
static void usb_audio_dev_intf_cleanup(struct usb_device *udev,
struct intf_info *info)
{
info->in_use = false;
}
static void uaudio_dev_cleanup(struct usb_audio_dev *dev)
{
int if_idx;
if (!dev) {
USB_OFFLOAD_ERR("USB audio device is already freed.\n");
return;
}
if (!dev->udev) {
USB_OFFLOAD_ERR("USB device is already freed.\n");
return;
}
/* free xfer buffer and unmap xfer ring and buf per interface */
for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
if (!dev->info[if_idx].in_use)
continue;
usb_audio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
USB_OFFLOAD_INFO("release resources: if_idx:%d intf# %d card# %d\n",
if_idx, dev->info[if_idx].intf_num, dev->card_num);
}
dev->num_intf = 0;
/* free interface info */
kfree(dev->info);
dev->info = NULL;
dev->udev = NULL;
}
int send_disconnect_ipi_msg_to_adsp(void)
{
int send_result = 0;
struct ipi_msg_t ipi_msg;
uint8_t scene = 0;
USB_OFFLOAD_INFO("\n");
// Send DISCONNECT msg to ADSP Via IPI
for (scene = TASK_SCENE_USB_DL; scene <= TASK_SCENE_USB_UL; scene++) {
send_result = audio_send_ipi_msg(
&ipi_msg, scene,
AUDIO_IPI_LAYER_TO_DSP,
AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_NEED_ACK,
AUD_USB_MSG_A2D_DISCONNECT,
0,
0,
NULL);
if (send_result == 0) {
send_result = ipi_msg.param2;
if (send_result)
break;
}
}
if (send_result != 0)
USB_OFFLOAD_ERR("USB Offload disconnect IPI msg send fail\n");
else
USB_OFFLOAD_INFO("USB Offload disconnect IPI msg send succeed\n");
return send_result;
}
static void uaudio_disconnect_cb(struct snd_usb_audio *chip)
{
int ret;
struct usb_audio_dev *dev;
int card_num = chip->card->number;
struct usb_audio_stream_msg msg = {0};
USB_OFFLOAD_INFO("for card# %d\n", card_num);
if (card_num >= SNDRV_CARDS) {
USB_OFFLOAD_ERR("invalid card number\n");
return;
}
mutex_lock(&uodev->dev_lock);
dev = &uadev[card_num];
/* clean up */
if (!dev->udev) {
USB_OFFLOAD_INFO("no clean up required\n");
goto done;
}
if (atomic_read(&dev->in_use)) {
mutex_unlock(&uodev->dev_lock);
msg.status = USB_AUDIO_STREAM_REQ_STOP;
msg.status_valid = 1;
/* write to audio ipi*/
ret = send_disconnect_ipi_msg_to_adsp();
/* wait response */
USB_OFFLOAD_INFO("send_disconnect_ipi_msg_to_adsp msg, ret: %d\n", ret);
atomic_set(&dev->in_use, 0);
mutex_lock(&uodev->dev_lock);
}
uaudio_dev_cleanup(dev);
done:
mutex_unlock(&uodev->dev_lock);
USB_OFFLOAD_INFO("done\n");
}
static void uaudio_dev_release(struct kref *kref)
{
struct usb_audio_dev *dev = container_of(kref, struct usb_audio_dev, kref);
if (!dev) {
USB_OFFLOAD_ERR("dev has been freed!!\n");
return;
}
USB_OFFLOAD_INFO("in_use:%d -> 0\n",
atomic_read(&uadev[uodev->card_num].in_use));
atomic_set(&dev->in_use, 0);
wake_up(&dev->disconnect_wq);
}
static int info_idx_from_ifnum(unsigned int card_num, int intf_num, bool enable)
{
int i;
USB_OFFLOAD_INFO("enable:%d, card_num:%d, intf_num:%d\n",
enable, card_num, intf_num);
/*
* default index 0 is used when info is allocated upon
* first enable audio stream req for a pcm device
*/
if (enable && !uadev[card_num].info) {
USB_OFFLOAD_INFO("enable:%d, uadev[%d].info:%d\n",
enable, card_num, uadev[card_num].info);
return 0;
}
USB_OFFLOAD_INFO("num_intf:%d\n", uadev[card_num].num_intf);
for (i = 0; i < uadev[card_num].num_intf; i++) {
USB_OFFLOAD_INFO("info_idx:%d, in_use:%d, intf_num:%d\n",
i,
uadev[card_num].info[i].in_use,
uadev[card_num].info[i].intf_num);
if (enable && !uadev[card_num].info[i].in_use)
return i;
else if (!enable &&
uadev[card_num].info[i].intf_num == intf_num)
return i;
}
return -EINVAL;
}
static int get_data_interval_from_si(struct snd_usb_substream *subs,
u32 service_interval)
{
unsigned int bus_intval, bus_intval_mult, binterval;
if (subs->dev->speed >= USB_SPEED_HIGH)
bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE;
else
bus_intval = BUS_INTERVAL_FULL_SPEED;
if (service_interval % bus_intval)
return -EINVAL;
bus_intval_mult = service_interval / bus_intval;
binterval = ffs(bus_intval_mult);
if (!binterval || binterval > MAX_BINTERVAL_ISOC_EP)
return -EINVAL;
/* check if another bit is set then bail out */
bus_intval_mult = bus_intval_mult >> binterval;
if (bus_intval_mult)
return -EINVAL;
return (binterval - 1);
}
/* looks up alias, if any, for controller DT node and returns the index */
static int usb_get_controller_alias_id(struct usb_device *udev)
{
if (udev->bus->sysdev && udev->bus->sysdev->of_node)
return of_alias_get_id(udev->bus->sysdev->of_node, "usb");
return -ENODEV;
}
static void *find_csint_desc(unsigned char *descstart, int desclen, u8 dsubtype)
{
u8 *p, *end, *next;
p = descstart;
end = p + desclen;
while (p < end) {
if (p[0] < 2)
return NULL;
next = p + p[0];
if (next > end)
return NULL;
if (p[1] == USB_DT_CS_INTERFACE && p[2] == dsubtype)
return p;
p = next;
}
return NULL;
}
static int usb_offload_prepare_msg(struct snd_usb_substream *subs,
struct usb_audio_stream_info *uainfo,
struct usb_audio_stream_msg *msg,
int info_idx)
{
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct usb_interface_assoc_descriptor *assoc;
struct usb_host_endpoint *ep;
struct uac_format_type_i_continuous_descriptor *fmt;
struct uac_format_type_i_discrete_descriptor *fmt_v1;
struct uac_format_type_i_ext_descriptor *fmt_v2;
struct uac1_as_header_descriptor *as;
int ret;
unsigned int protocol, card_num, pcm_dev_num;
int interface, altset_idx;
void *hdr_ptr;
unsigned int data_ep_pipe = 0, sync_ep_pipe = 0;
if (subs == NULL) {
USB_OFFLOAD_ERR("substream is NULL!\n");
ret = -ENODEV;
goto err;
}
if (subs->cur_audiofmt == NULL) {
USB_OFFLOAD_ERR("substream->cur_audio_fmt is NULL!\n");
ret = -ENODEV;
goto err;
}
interface = subs->cur_audiofmt->iface;
altset_idx = subs->cur_audiofmt->altset_idx;
iface = usb_ifnum_to_if(subs->dev, interface);
if (!iface) {
USB_OFFLOAD_ERR("interface # %d does not exist\n", interface);
ret = -ENODEV;
goto err;
}
msg->uainfo = *uainfo;
assoc = iface->intf_assoc;
pcm_dev_num = uainfo->pcm_dev_num;
card_num = uainfo->pcm_card_num;
msg->direction = uainfo->direction;
msg->pcm_dev_num = uainfo->pcm_dev_num;
msg->pcm_card_num = uainfo->pcm_card_num;
alts = &iface->altsetting[altset_idx];
altsd = get_iface_desc(alts);
protocol = altsd->bInterfaceProtocol;
/* get format type */
if (protocol != UAC_VERSION_3) {
fmt = find_csint_desc(alts->extra, alts->extralen,
UAC_FORMAT_TYPE);
if (!fmt) {
USB_OFFLOAD_ERR("%u:%d : no UAC_FORMAT_TYPE desc\n",
interface, altset_idx);
ret = -ENODEV;
goto err;
}
}
if (!uadev[card_num].ctrl_intf) {
USB_OFFLOAD_ERR("audio ctrl intf info not cached\n");
ret = -ENODEV;
goto err;
}
if (protocol != UAC_VERSION_3) {
hdr_ptr = find_csint_desc(uadev[card_num].ctrl_intf->extra,
uadev[card_num].ctrl_intf->extralen,
UAC_HEADER);
if (!hdr_ptr) {
USB_OFFLOAD_ERR("no UAC_HEADER desc\n");
ret = -ENODEV;
goto err;
}
}
if (protocol == UAC_VERSION_1) {
struct uac1_ac_header_descriptor *uac1_hdr = hdr_ptr;
as = find_csint_desc(alts->extra, alts->extralen,
UAC_AS_GENERAL);
if (!as) {
USB_OFFLOAD_ERR("%u:%d : no UAC_AS_GENERAL desc\n",
interface, altset_idx);
ret = -ENODEV;
goto err;
}
msg->data_path_delay = as->bDelay;
msg->data_path_delay_valid = 1;
fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt;
msg->usb_audio_subslot_size = fmt_v1->bSubframeSize;
msg->usb_audio_subslot_size_valid = 1;
msg->usb_audio_spec_revision = le16_to_cpu(uac1_hdr->bcdADC);
msg->usb_audio_spec_revision_valid = 1;
} else if (protocol == UAC_VERSION_2) {
struct uac2_ac_header_descriptor *uac2_hdr = hdr_ptr;
fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt;
msg->usb_audio_subslot_size = fmt_v2->bSubslotSize;
msg->usb_audio_subslot_size_valid = 1;
msg->usb_audio_spec_revision = le16_to_cpu(uac2_hdr->bcdADC);
msg->usb_audio_spec_revision_valid = 1;
} else if (protocol == UAC_VERSION_3) {
if (assoc->bFunctionSubClass ==
UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0) {
USB_OFFLOAD_ERR("full adc is not supported\n");
ret = -EINVAL;
}
switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: {
msg->usb_audio_subslot_size = 0x2;
break;
}
case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: {
msg->usb_audio_subslot_size = 0x3;
break;
}
default:
USB_OFFLOAD_ERR("%d: %u: Invalid wMaxPacketSize\n",
interface, altset_idx);
ret = -EINVAL;
goto err;
}
msg->usb_audio_subslot_size_valid = 1;
} else {
USB_OFFLOAD_ERR("unknown protocol version %x\n", protocol);
ret = -ENODEV;
goto err;
}
msg->slot_id = subs->dev->slot_id;
msg->slot_id_valid = 1;
memcpy(&msg->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
msg->std_as_opr_intf_desc_valid = 1;
ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe);
if (!ep) {
USB_OFFLOAD_ERR("data ep # %d context is null\n",
subs->data_endpoint->ep_num);
ret = -ENODEV;
goto err;
}
data_ep_pipe = subs->data_endpoint->pipe;
memcpy(&msg->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
msg->std_as_data_ep_desc_valid = 1;
if (subs->sync_endpoint) {
ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe);
if (!ep) {
USB_OFFLOAD_ERR("implicit fb on data ep\n");
goto skip_sync_ep;
}
sync_ep_pipe = subs->sync_endpoint->pipe;
memcpy(&msg->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
msg->std_as_sync_ep_desc_valid = 1;
}
skip_sync_ep:
msg->interrupter_num = uodev->intr_num;
msg->interrupter_num_valid = 1;
msg->controller_num_valid = 0;
ret = usb_get_controller_alias_id(subs->dev);
if (ret >= 0) {
msg->controller_num = ret;
msg->controller_num_valid = 1;
}
msg->speed_info = get_speed_info(subs->dev->speed);
if (msg->speed_info == USB_AUDIO_DEVICE_SPEED_INVALID) {
ret = -ENODEV;
goto err;
}
msg->speed_info_valid = 1;
if (!atomic_read(&uadev[card_num].in_use)) {
kref_init(&uadev[card_num].kref);
init_waitqueue_head(&uadev[card_num].disconnect_wq);
uadev[card_num].num_intf =
subs->dev->config->desc.bNumInterfaces;
uadev[card_num].info = kcalloc(uadev[card_num].num_intf,
sizeof(struct intf_info), GFP_KERNEL);
if (!uadev[card_num].info) {
ret = -ENOMEM;
goto err;
}
uadev[card_num].udev = subs->dev;
atomic_set(&uadev[card_num].in_use, 1);
} else {
kref_get(&uadev[card_num].kref);
}
uadev[card_num].card_num = card_num;
uadev[card_num].usb_core_id = msg->controller_num;
uadev[card_num].info[info_idx].data_ep_pipe = data_ep_pipe;
uadev[card_num].info[info_idx].sync_ep_pipe = sync_ep_pipe;
uadev[card_num].info[info_idx].pcm_card_num = card_num;
uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
uadev[card_num].info[info_idx].direction = subs->direction;
uadev[card_num].info[info_idx].intf_num = interface;
uadev[card_num].info[info_idx].in_use = true;
set_bit(card_num, &uodev->card_slot);
uodev->card_num = card_num;
return 0;
err:
return ret;
}
int send_init_ipi_msg_to_adsp(struct mem_info_xhci *mpu_info)
{
int send_result = 0;
struct ipi_msg_t ipi_msg;
uint8_t scene = 0;
USB_OFFLOAD_INFO("xhci_rsv_addr: 0x%x, xhci_rsv_size: %d, size: %d\n",
mpu_info->xhci_data_addr,
mpu_info->xhci_data_size,
sizeof(*mpu_info));
// Send struct usb_audio_stream_info Address to Hifi3 Via IPI
for (scene = TASK_SCENE_USB_DL; scene <= TASK_SCENE_USB_UL; scene++) {
send_result = audio_send_ipi_msg(
&ipi_msg, scene,
AUDIO_IPI_LAYER_TO_DSP,
AUDIO_IPI_PAYLOAD,
AUDIO_IPI_MSG_NEED_ACK,
AUD_USB_MSG_A2D_INIT_ADSP,
sizeof(struct mem_info_xhci),
0,
mpu_info);
if (send_result == 0) {
send_result = ipi_msg.param2;
if (send_result)
break;
}
}
if (send_result != 0)
USB_OFFLOAD_ERR("USB Offload init IPI msg send fail\n");
else
USB_OFFLOAD_INFO("USB Offload init IPI msg send succeed\n");
return send_result;
}
int send_uas_ipi_msg_to_adsp(struct usb_audio_stream_msg *uas_msg)
{
int send_result = 0;
struct ipi_msg_t ipi_msg;
uint8_t task_scene = 0;
USB_OFFLOAD_INFO("msg: %p, size: %d\n",
uas_msg, sizeof(*uas_msg));
if (uas_msg->uainfo.direction == 0)
task_scene = TASK_SCENE_USB_DL;
else
task_scene = TASK_SCENE_USB_UL;
// Send struct usb_audio_stream_info Address to ADSP Via IPI
send_result = audio_send_ipi_msg(
&ipi_msg, task_scene,
AUDIO_IPI_LAYER_TO_DSP,
AUDIO_IPI_DMA,
AUDIO_IPI_MSG_NEED_ACK,
AUD_USB_MSG_A2D_ENABLE_STREAM,
sizeof(struct usb_audio_stream_msg),
0,
uas_msg);
if (send_result == 0)
send_result = ipi_msg.param2;
if (send_result != 0)
USB_OFFLOAD_ERR("USB Offload uas IPI msg send fail\n");
else
USB_OFFLOAD_INFO("USB Offload uas ipi msg send succeed\n");
return send_result;
}
static int usb_offload_enable_stream(struct usb_audio_stream_info *uainfo)
{
struct usb_audio_stream_msg msg = {0};
struct snd_usb_substream *subs;
struct snd_pcm_substream *substream;
struct snd_usb_audio *chip = NULL;
struct intf_info *info;
struct usb_host_endpoint *ep;
u8 pcm_card_num, pcm_dev_num, direction;
int info_idx = -EINVAL, datainterval = -EINVAL, ret = 0;
int interface;
direction = uainfo->direction;
pcm_dev_num = uainfo->pcm_dev_num;
pcm_card_num = uainfo->pcm_card_num;
USB_OFFLOAD_INFO("direction: %d, pcm_dev_num: %d, pcm_card_num: %d\n",
direction, pcm_dev_num, pcm_card_num);
if (pcm_card_num >= SNDRV_CARDS) {
USB_OFFLOAD_ERR("invalid card # %u", pcm_card_num);
ret = -EINVAL;
goto done;
}
if (!is_support_format(uainfo->audio_format)) {
USB_OFFLOAD_ERR("unsupported pcm format received %d\n",
uainfo->audio_format);
ret = -EINVAL;
goto done;
}
subs = find_snd_usb_substream(pcm_card_num, pcm_dev_num, direction,
&chip, uaudio_disconnect_cb);
if (!subs || !chip || atomic_read(&chip->shutdown)) {
USB_OFFLOAD_ERR("can't find substream for card# %u, dev# %u, dir: %u\n",
pcm_card_num, pcm_dev_num, direction);
ret = -ENODEV;
goto done;
}
mutex_lock(&uodev->dev_lock);
USB_OFFLOAD_INFO("inside mutex\n");
if (subs->cur_audiofmt)
interface = subs->cur_audiofmt->iface;
else
interface = -1;
info_idx = info_idx_from_ifnum(pcm_card_num, interface,
uainfo->enable);
USB_OFFLOAD_INFO("info_idx: %d, interface: %d\n",
info_idx, interface);
if (uainfo->enable) {
if (info_idx < 0) {
USB_OFFLOAD_ERR("interface# %d already in use card# %d\n",
interface, pcm_card_num);
ret = -EBUSY;
mutex_unlock(&uodev->dev_lock);
goto done;
}
}
if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm
|| !subs->stream->chip || !subs->pcm_substream || info_idx < 0) {
USB_OFFLOAD_INFO("chip->shutdown:%d\n", atomic_read(&chip->shutdown));
if (!subs->stream)
USB_OFFLOAD_INFO("NO subs->stream\n");
else {
if (!subs->stream->pcm)
USB_OFFLOAD_INFO("NO subs->stream->pcm\n");
if (!subs->stream->chip)
USB_OFFLOAD_INFO("NO subs->stream->chip\n");
}
if (!subs->pcm_substream)
USB_OFFLOAD_INFO("NO subs->pcm_substream\n");
ret = -ENODEV;
mutex_unlock(&uodev->dev_lock);
goto done;
}
if (uainfo->service_interval_valid) {
ret = get_data_interval_from_si(subs,
uainfo->service_interval);
if (ret == -EINVAL) {
USB_OFFLOAD_ERR("invalid service interval %u\n",
uainfo->service_interval);
mutex_unlock(&uodev->dev_lock);
goto done;
}
datainterval = ret;
USB_OFFLOAD_INFO("data interval %u\n", ret);
}
uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
USB_OFFLOAD_INFO("uainfo->enable:%d\n", uainfo->enable);
if (!uainfo->enable) {
info = &uadev[pcm_card_num].info[info_idx];
if (info->data_ep_pipe) {
ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
info->data_ep_pipe);
if (!ep)
USB_OFFLOAD_ERR("no data ep\n");
else
USB_OFFLOAD_INFO("stop data ep\n");
info->data_ep_pipe = 0;
}
if (info->sync_ep_pipe) {
ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
info->sync_ep_pipe);
if (!ep)
USB_OFFLOAD_ERR("no sync ep\n");
else
USB_OFFLOAD_INFO("stop sync ep\n");
info->sync_ep_pipe = 0;
}
}
substream = subs->pcm_substream;
if (!substream->ops->hw_params || !substream->ops->hw_free
|| !substream->ops->prepare) {
USB_OFFLOAD_ERR("no hw_params/hw_free/prepare ops\n");
ret = -ENODEV;
mutex_unlock(&uodev->dev_lock);
goto done;
}
if (uainfo->enable) {
ret = usb_offload_prepare_msg(subs, uainfo, &msg, info_idx);
USB_OFFLOAD_INFO("prepare msg, ret: %d\n", ret);
if (ret < 0) {
mutex_unlock(&uodev->dev_lock);
return ret;
}
} else {
ret = substream->ops->hw_free(substream);
USB_OFFLOAD_INFO("hw_free, ret: %d\n", ret);
msg.uainfo.direction = uainfo->direction;
}
mutex_unlock(&uodev->dev_lock);
msg.status = uainfo->enable ?
USB_AUDIO_STREAM_REQ_START : USB_AUDIO_STREAM_REQ_STOP;
/* write to audio ipi*/
ret = send_uas_ipi_msg_to_adsp(&msg);
USB_OFFLOAD_INFO("send_ipi_msg_to_adsp msg, ret: %d\n", ret);
/* wait response */
if(uainfo->enable && !ret) {
/* remove the qos request */
if (pm_qos_request_active(&subs->pm_qos)) {
pm_qos_remove_request(&subs->pm_qos);
USB_OFFLOAD_INFO("(pm_qos @%p) remove\n", &subs->pm_qos);
} else
USB_OFFLOAD_INFO("(pm_qos @%p) not active\n", &subs->pm_qos);
}
done:
if ((!uainfo->enable && ret != -EINVAL && ret != -ENODEV) ||
(uainfo->enable && ret == -ENODEV)) {
mutex_lock(&uodev->dev_lock);
if (info_idx >= 0) {
if (!uadev[pcm_card_num].info) {
USB_OFFLOAD_ERR("uaudio_dev cleanup already!\n");
mutex_unlock(&uodev->dev_lock);
return ret;
}
info = &uadev[pcm_card_num].info[info_idx];
usb_audio_dev_intf_cleanup(
uadev[pcm_card_num].udev,
info);
USB_OFFLOAD_INFO("release resources: intf# %d card# %d\n",
interface, pcm_card_num);
}
if (atomic_read(&uadev[pcm_card_num].in_use))
kref_put(&uadev[pcm_card_num].kref, uaudio_dev_release);
mutex_unlock(&uodev->dev_lock);
}
return ret;
}
static struct usb_offload_mem_info usb_offload_mem_buffer[USB_OFFLOAD_MEM_NUM];
static struct gen_pool *usb_offload_mem_pool[USB_OFFLOAD_MEM_NUM];
static int dump_mtk_usb_offload_gen_pool(void)
{
int i = 0;
for (i = 0; i < USB_OFFLOAD_MEM_NUM; i++) {
USB_OFFLOAD_INFO("idx: %d, gen_pool_avail: %zu, gen_pool_size: %zu\n",
i,
gen_pool_avail(usb_offload_mem_pool[i]),
gen_pool_size(usb_offload_mem_pool[i]));
if (!uodev->default_use_sram)
break;
}
return 0;
}
struct gen_pool *mtk_get_usb_offload_mem_gen_pool(enum usb_offload_mem_id mem_id)
{
if (mem_id < USB_OFFLOAD_MEM_NUM)
return usb_offload_mem_pool[mem_id];
USB_OFFLOAD_ERR("Invalid id: %d\n", mem_id);
return NULL;
}
EXPORT_SYMBOL_GPL(mtk_get_usb_offload_mem_gen_pool);
static int mtk_init_usb_offload_sharemem(uint32_t dram_mem_id, uint32_t mem_id)
{
int ret = 0;
unsigned long long sram_tr_addr, sram_tr_size;
switch (mem_id) {
case USB_OFFLOAD_MEM_DRAM_ID:
if (!adsp_get_reserve_mem_phys(dram_mem_id))
return -EPROBE_DEFER;
usb_offload_mem_buffer[mem_id].phy_addr = adsp_get_reserve_mem_phys(dram_mem_id);
usb_offload_mem_buffer[mem_id].va_addr =
(unsigned long long) adsp_get_reserve_mem_virt(dram_mem_id);
usb_offload_mem_buffer[mem_id].vir_addr = adsp_get_reserve_mem_virt(dram_mem_id);
usb_offload_mem_buffer[mem_id].size = adsp_get_reserve_mem_size(dram_mem_id);
break;
case USB_OFFLOAD_MEM_SRAM_ID:
sram_tr_addr = SRAM_ADDR + SRAM_TR_OFST;
sram_tr_size = SRAM_TR_SIZE;
USB_OFFLOAD_INFO("sram_tr_addr: 0x%llx, sram_tr_size: %llu",
sram_tr_addr, sram_tr_size);
usb_offload_mem_buffer[mem_id].phy_addr = sram_tr_addr;
usb_offload_mem_buffer[mem_id].va_addr =
(unsigned long long) ioremap_wc(
(phys_addr_t) sram_tr_addr, (unsigned long) sram_tr_size);
usb_offload_mem_buffer[mem_id].vir_addr = (unsigned char *) sram_tr_addr;
usb_offload_mem_buffer[mem_id].size = sram_tr_size;
break;
}
USB_OFFLOAD_INFO("mem_id(%u), buf_id(%u), phy_addr(0x%llx), vir_addr(%p)\n",
dram_mem_id, mem_id,
usb_offload_mem_buffer[mem_id].phy_addr,
usb_offload_mem_buffer[mem_id].vir_addr);
USB_OFFLOAD_INFO("va_addr:(0x%llx), size(%llu)\n",
usb_offload_mem_buffer[mem_id].va_addr,
usb_offload_mem_buffer[mem_id].size);
return ret;
}
static int mtk_usb_offload_gen_pool_create(int min_alloc_order, int nid)
{
int i, ret = 0;
unsigned long va_start;
size_t va_chunk;
if (min_alloc_order <= 0)
return -1;
USB_OFFLOAD_MEM_DBG("\n");
for (i = 0; i < USB_OFFLOAD_MEM_NUM; i++) {
usb_offload_mem_pool[i] = gen_pool_create(min_alloc_order, -1);
if (!usb_offload_mem_pool[i])
return -ENOMEM;
va_start = usb_offload_mem_buffer[i].va_addr;
va_chunk = usb_offload_mem_buffer[i].size;
if ((!va_start) || (!va_chunk)) {
ret = -1;
break;
}
if (gen_pool_add_virt(usb_offload_mem_pool[i], (unsigned long)va_start,
usb_offload_mem_buffer[i].phy_addr, va_chunk, -1)) {
USB_OFFLOAD_ERR("idx: %d failed, va_start: 0x%lx, va_chunk: %zu\n",
i, va_start, va_chunk);
}
USB_OFFLOAD_MEM_DBG("idx:%d success, va_start:0x%lx, va_chunk:%zu, pool[%d]:%p\n",
i, va_start, va_chunk, i, usb_offload_mem_pool[i]);
if (!uodev->default_use_sram)
break;
}
dump_mtk_usb_offload_gen_pool();
return ret;
}
static int mtk_usb_offload_genpool_allocate_memory(unsigned char **vaddr,
dma_addr_t *paddr,
unsigned int size,
enum usb_offload_mem_id mem_id,
int align)
{
/* gen pool related */
struct gen_pool *gen_pool_usb_offload = mtk_get_usb_offload_mem_gen_pool(mem_id);
if (gen_pool_usb_offload == NULL) {
USB_OFFLOAD_ERR("gen_pool_usb_offload == NULL\n");
return -1;
}
USB_OFFLOAD_MEM_DBG("mem_id: %d, vaddr: %p, DMA paddr: 0x%llx, size:%u\n",
mem_id, vaddr, (unsigned long long)*paddr, size);
/* allocate VA with gen pool */
if (*vaddr == NULL) {
*vaddr = (unsigned char *)gen_pool_alloc(gen_pool_usb_offload, size);
if (*vaddr)
memset(*vaddr, 0, size);
*paddr = gen_pool_virt_to_phys(gen_pool_usb_offload, (unsigned long)*vaddr);
}
USB_OFFLOAD_MEM_DBG("size: %u, id: %d, vaddr: %p, DMA paddr: 0x%llx\n",
size, mem_id, vaddr, (unsigned long long)*paddr);
return 0;
}
static int mtk_usb_offload_genpool_free_memory(unsigned char **vaddr,
size_t *size, enum usb_offload_mem_id mem_id)
{
/* gen pool related */
struct gen_pool *gen_pool_usb_offload = mtk_get_usb_offload_mem_gen_pool(mem_id);
if (gen_pool_usb_offload == NULL) {
USB_OFFLOAD_ERR("gen_pool_usb_offload == NULL\n");
return -1;
}
/*gen_pool_has_addr --> addr_in_gen_pool*/
if (!addr_in_gen_pool(gen_pool_usb_offload, (unsigned long)*vaddr, *size)) {
USB_OFFLOAD_ERR("vaddr is not in genpool\n");
return -1;
}
/* allocate VA with gen pool */
if (*vaddr) {
USB_OFFLOAD_MEM_DBG("size: %u, id: %d, vaddr: %p\n",
size, mem_id, vaddr);
gen_pool_free(gen_pool_usb_offload, (unsigned long)*vaddr, *size);
*vaddr = NULL;
*size = 0;
}
return 0;
}
int mtk_usb_offload_allocate_mem(struct usb_offload_buffer *buf,
unsigned int size, int align, enum usb_offload_mem_id mem_id)
{
int ret = 0;
if (buf->dma_area) {
ret = mtk_usb_offload_genpool_free_memory(
&buf->dma_area,
&buf->dma_bytes,
mem_id);
if (ret)
USB_OFFLOAD_ERR("Fail to free memoroy\n");
}
ret = mtk_usb_offload_genpool_allocate_memory
(&buf->dma_area,
&buf->dma_addr,
size,
mem_id,
align);
if (!ret) {
buf->dma_bytes = size;
buf->allocated = true;
}
return ret;
}
EXPORT_SYMBOL_GPL(mtk_usb_offload_allocate_mem);
int mtk_usb_offload_free_mem(struct usb_offload_buffer *buf, enum usb_offload_mem_id mem_id)
{
int ret = 0;
ret = mtk_usb_offload_genpool_free_memory(
&buf->dma_area,
&buf->dma_bytes,
mem_id);
if (!ret) {
buf->dma_addr = 0;
buf->allocated = 0;
}
return ret;
}
EXPORT_SYMBOL_GPL(mtk_usb_offload_free_mem);
static bool xhci_mtk_is_usb_offload_enabled(struct xhci_hcd *xhci,
struct xhci_virt_device *vdev,
unsigned int ep_index)
{
return true;
}
static struct xhci_device_context_array *xhci_mtk_alloc_dcbaa(struct xhci_hcd *xhci,
gfp_t flags)
{
struct xhci_device_context_array *xhci_ctx;
buf_dcbaa = kzalloc(sizeof(struct usb_offload_buffer), GFP_KERNEL);
if (mtk_usb_offload_allocate_mem(buf_dcbaa, sizeof(*xhci_ctx), 64,
USB_OFFLOAD_MEM_DRAM_ID)) {
USB_OFFLOAD_ERR("FAIL to allocate mem for USB Offload DCBAA\n");
return NULL;
}
USB_OFFLOAD_MEM_DBG("size of dcbaa: %d\n", sizeof(*xhci_ctx));
xhci_ctx = (struct xhci_device_context_array *) buf_dcbaa->dma_area;
xhci_ctx->dma = buf_dcbaa->dma_addr;
USB_OFFLOAD_MEM_DBG("xhci_ctx.dev_context_ptrs:%p xhci_ctx.dma:%llx\n",
xhci_ctx->dev_context_ptrs, xhci_ctx->dma);
buf_ctx = kzalloc(sizeof(struct usb_offload_buffer) * BUF_CTX_SIZE, GFP_KERNEL);
buf_seg = kzalloc(sizeof(struct usb_offload_buffer) * BUF_SEG_SIZE, GFP_KERNEL);
return xhci_ctx;
}
static void xhci_mtk_free_dcbaa(struct xhci_hcd *xhci)
{
if (!buf_dcbaa) {
USB_OFFLOAD_ERR("DCBAA has not been initialized.\n");
return;
}
if (mtk_usb_offload_free_mem(buf_dcbaa, USB_OFFLOAD_MEM_DRAM_ID))
USB_OFFLOAD_ERR("FAIL to free mem for USB Offload DCBAA\n");
else
USB_OFFLOAD_MEM_DBG("Free mem DCBAA DONE\n");
kfree(buf_seg);
kfree(buf_ctx);
kfree(buf_dcbaa);
buf_seg = NULL;
buf_ctx = NULL;
buf_dcbaa = NULL;
}
static int get_first_avail_buf_ctx_idx(struct xhci_hcd *xhci)
{
unsigned int idx;
for (idx = 0; idx <= BUF_CTX_SIZE; idx++) {
USB_OFFLOAD_MEM_DBG("idx: %d, alloc: %d, DMA area: %p, addr: %llx, bytes: %d\n",
idx,
buf_ctx[idx].allocated,
buf_ctx[idx].dma_area,
buf_ctx[idx].dma_addr,
buf_ctx[idx].dma_bytes);
if (!buf_ctx[idx].allocated)
return idx;
}
USB_OFFLOAD_ERR("NO Available BUF Context.\n");
return 0;
}
static void xhci_mtk_alloc_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
int type, gfp_t flags)
{
int buf_ctx_slot = get_first_avail_buf_ctx_idx(xhci);
if (mtk_usb_offload_allocate_mem(&buf_ctx[buf_ctx_slot], ctx->size, 64,
USB_OFFLOAD_MEM_DRAM_ID)) {
USB_OFFLOAD_ERR("FAIL to allocate mem for USB Offload Context %d size: %d\n",
buf_ctx_slot, ctx->size);
return;
}
USB_OFFLOAD_MEM_DBG("Success allocated mem for USB Offload Context %d\n", buf_ctx_slot);
ctx->bytes = buf_ctx[buf_ctx_slot].dma_area;
ctx->dma = buf_ctx[buf_ctx_slot].dma_addr;
USB_OFFLOAD_MEM_DBG("ctx.bytes: %p, ctx.dma: %llx, ctx.size: %d\n",
ctx->bytes, ctx->dma, ctx->size);
}
static void xhci_mtk_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
{
unsigned int idx;
for (idx = 0; idx < BUF_CTX_SIZE; idx++) {
USB_OFFLOAD_MEM_DBG("ctx[%d], alloc: %d, dma_addr: %llx, dma: %llx\n",
idx,
buf_ctx[idx].allocated,
buf_ctx[idx].dma_addr,
ctx->dma);
if (buf_ctx[idx].allocated && buf_ctx[idx].dma_addr == ctx->dma) {
if (mtk_usb_offload_free_mem(&buf_ctx[idx], USB_OFFLOAD_MEM_DRAM_ID))
USB_OFFLOAD_ERR("FAIL: free mem ctx: %d\n", idx);
else
USB_OFFLOAD_MEM_DBG("Free mem ctx: %d DONE\n", idx);
return;
}
}
USB_OFFLOAD_MEM_DBG("NO Context MATCH to be freed. ctx.bytes:%p ctx.dma:%llx ctx.size:%d\n",
ctx->bytes,
ctx->dma,
ctx->size);
}
static int get_first_avail_buf_seg_idx(void)
{
unsigned int idx;
for (idx = 0; idx < BUF_SEG_SIZE; idx++) {
USB_OFFLOAD_MEM_DBG("seg[%d], alloc: %d, DMA area: %p, addr: %llx, bytes: %d\n",
idx,
buf_seg[idx].allocated,
buf_seg[idx].dma_area,
buf_seg[idx].dma_addr,
buf_seg[idx].dma_bytes);
if (!buf_seg[idx].allocated)
return idx;
}
USB_OFFLOAD_ERR("NO Available BUF Segment.\n");
return 0;
}
static void xhci_mtk_usb_offload_segment_free(struct xhci_hcd *xhci,
struct xhci_segment *seg, enum usb_offload_mem_id mem_id)
{
unsigned int idx;
if (seg->trbs) {
for (idx = 0; idx < BUF_SEG_SIZE; idx++) {
USB_OFFLOAD_MEM_DBG("seg[%d], alloc:%d, dma_addr:%llx, dma:%llx, size:%d\n",
idx,
buf_seg[idx].allocated,
buf_seg[idx].dma_addr,
seg->dma,
buf_seg[idx].dma_bytes);
if (buf_seg[idx].allocated && buf_seg[idx].dma_addr == seg->dma) {
if (mtk_usb_offload_free_mem(&buf_seg[idx], mem_id))
USB_OFFLOAD_ERR("FAIL: free mem seg: %d\n", idx);
else
USB_OFFLOAD_MEM_DBG("Free mem seg: %d DONE\n", idx);
goto done;
}
}
USB_OFFLOAD_MEM_DBG("NO Segment MATCH to be freed. seg->trbs: %p, seg->dma: %llx\n",
seg->trbs, seg->dma);
done:
seg->trbs = NULL;
}
kfree(seg->bounce_buf);
kfree(seg);
}
static void xhci_mtk_usb_offload_free_segments_for_ring(struct xhci_hcd *xhci,
struct xhci_segment *first, enum usb_offload_mem_id mem_id)
{
struct xhci_segment *seg;
seg = first->next;
while (seg != first) {
struct xhci_segment *next = seg->next;
xhci_mtk_usb_offload_segment_free(xhci, seg, mem_id);
seg = next;
}
xhci_mtk_usb_offload_segment_free(xhci, first, mem_id);
}
/*
* Allocates a generic ring segment from the ring pool, sets the dma address,
* initializes the segment to zero, and sets the private next pointer to NULL.
*
* Section 4.11.1.1:
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
*/
static struct xhci_segment *xhci_mtk_usb_offload_segment_alloc(struct xhci_hcd *xhci,
unsigned int cycle_state,
unsigned int max_packet,
gfp_t flags,
enum usb_offload_mem_id mem_id)
{
struct xhci_segment *seg;
dma_addr_t dma;
int i;
int buf_seg_slot = get_first_avail_buf_seg_idx();
seg = kzalloc(sizeof(*seg), flags);
if (!seg)
return NULL;
if (mtk_usb_offload_allocate_mem(&buf_seg[buf_seg_slot],
USB_OFFLOAD_TRB_SEGMENT_SIZE, USB_OFFLOAD_TRB_SEGMENT_SIZE, mem_id)) {
USB_OFFLOAD_ERR("FAIL to allocate mem id: %d for USB Offload Seg %d, size: %d\n",
mem_id, buf_seg_slot, USB_OFFLOAD_TRB_SEGMENT_SIZE);
kfree(seg);
return NULL;
}
USB_OFFLOAD_MEM_DBG("Success allocated mem id: %d for USB Offload Seg %d\n",
mem_id, buf_seg_slot);
seg->trbs = (void *) buf_seg[buf_seg_slot].dma_area;
seg->dma = 0;
dma = buf_seg[buf_seg_slot].dma_addr;
USB_OFFLOAD_MEM_DBG("seg->trbs: %p, dma: %llx, size: %d\n",
seg->trbs,
dma,
sizeof(buf_seg[buf_seg_slot]));
if (!seg->trbs) {
USB_OFFLOAD_ERR("No seg->trbs\n");
kfree(seg);
return NULL;
}
if (max_packet) {
seg->bounce_buf = kzalloc(max_packet, flags);
if (!seg->bounce_buf) {
xhci_mtk_usb_offload_segment_free(xhci, seg, mem_id);
return NULL;
}
}
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
if (cycle_state == 0) {
for (i = 0; i < USB_OFFLOAD_TRBS_PER_SEGMENT; i++)
seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
}
seg->dma = dma;
seg->next = NULL;
return seg;
}
/* Allocate segments and link them for a ring */
static int xhci_mtk_usb_offload_alloc_segments_for_ring(struct xhci_hcd *xhci,
struct xhci_segment **first, struct xhci_segment **last,
unsigned int num_segs, unsigned int cycle_state,
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags,
enum usb_offload_mem_id mem_id)
{
struct xhci_segment *prev;
bool chain_links;
USB_OFFLOAD_MEM_DBG("mem_id: %d, ring->first_seg: %p, ring->last_seg: %p\n",
mem_id, first, last);
USB_OFFLOAD_MEM_DBG("num_segs: %d, cycle_state: %d, ring_type: %d, max_packet: %d\n",
num_segs, cycle_state, type, max_packet);
/* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
chain_links = !!(xhci_link_trb_quirk(xhci) ||
(type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)));
prev = xhci_mtk_usb_offload_segment_alloc(xhci, cycle_state, max_packet, flags, mem_id);
if (!prev)
return -ENOMEM;
num_segs--;
*first = prev;
while (num_segs > 0) {
struct xhci_segment *next;
next = xhci_mtk_usb_offload_segment_alloc(
xhci, cycle_state, max_packet, flags, mem_id);
if (!next) {
prev = *first;
while (prev) {
next = prev->next;
xhci_mtk_usb_offload_segment_free(xhci, prev, mem_id);
prev = next;
}
return -ENOMEM;
}
xhci_link_segments(xhci, prev, next, type/*, chain_links*/);
prev = next;
num_segs--;
}
xhci_link_segments(xhci, prev, *first, type/*, chain_links*/);
*last = prev;
return 0;
}
static struct xhci_ring *xhci_mtk_alloc_transfer_ring(struct xhci_hcd *xhci,
u32 endpoint_type, enum xhci_ring_type ring_type,
unsigned int max_packet, gfp_t mem_flags)
{
struct xhci_ring *ring;
int ret;
int num_segs = 1;
int cycle_state = 1;
ring = kzalloc(sizeof(*ring), mem_flags);
if (!ring)
return NULL;
if (ring_type == TYPE_ISOC)
num_segs = 2;
ring->num_segs = num_segs;
ring->bounce_buf_len = max_packet;
INIT_LIST_HEAD(&ring->td_list);
ring->type = ring_type;
ret = xhci_mtk_usb_offload_alloc_segments_for_ring(xhci, &ring->first_seg,
&ring->last_seg, num_segs, cycle_state, ring_type,
max_packet, mem_flags, uodev->mem_id);
if (ret) {
USB_OFFLOAD_ERR("Fail to alloc segment for rings (mem_id:%d)\n", uodev->mem_id);
goto fail;
}
if (ring_type != TYPE_EVENT) {
/* See section 4.9.2.1 and 6.4.4.1 */
ring->last_seg->trbs[USB_OFFLOAD_TRBS_PER_SEGMENT - 1].link.control |=
cpu_to_le32(LINK_TOGGLE);
}
xhci_initialize_ring_info(ring, cycle_state);
//trace_xhci_ring_alloc(ring);
return ring;
fail:
kfree(ring);
return NULL;
}
static void xhci_mtk_free_transfer_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, unsigned int ep_index)
{
struct xhci_ring *ring = virt_dev->eps[ep_index].ring;
if (!ring)
return;
if (ring->first_seg)
xhci_mtk_usb_offload_free_segments_for_ring(xhci, ring->first_seg, uodev->mem_id);
kfree(ring);
}
static bool xhci_mtk_is_streaming(struct xhci_hcd *xhci)
{
USB_OFFLOAD_INFO("is_streaming: %d\n", uodev->is_streaming);
return uodev->is_streaming;
}
static int check_usb_offload_quirk(int vid, int pid)
{
if (vid == 0x046D && pid == 0x0A38) {
USB_OFFLOAD_INFO("Logitech USB Headset H340 NOT SUPPORT!!\n");
return -1;
}
if (vid == 0x0BDA && pid == 0x4BD1) {
USB_OFFLOAD_INFO("JOWOYE MH339 NOT SUPPORT!!\n");
return -1;
}
return 0;
}
static int check_is_multiple_ep(struct usb_host_config *config)
{
struct usb_interface *intf;
struct usb_host_interface *hostif;
struct usb_interface_descriptor *intfd;
int i, j;
if (!config)
return -1;
USB_OFFLOAD_INFO("num of intf: %d\n", config->desc.bNumInterfaces);
for (i = 0; i < config->desc.bNumInterfaces; i++) {
intf = config->interface[i];
for (j = 0; j < intf->num_altsetting; j++) {
if (!uodev->connected) {
USB_OFFLOAD_ERR("No dev(%d)\n",
uodev->connected);
return -1;
}
hostif = &intf->altsetting[j];
if (!hostif) {
USB_OFFLOAD_ERR("No alt(%d)\n",
uodev->connected);
return -1;
}
intfd = get_iface_desc(hostif);
if (!intfd) {
USB_OFFLOAD_ERR("No intf desc(%d)\n",
uodev->connected);
return -1;
}
USB_OFFLOAD_INFO("intf:%d, alt:%d, numEP: %d, class:0x%x, sub:0x%x\n",
i, j,
intfd->bNumEndpoints,
intfd->bInterfaceClass,
intfd->bInterfaceSubClass);
if (intfd->bNumEndpoints > 1 &&
intfd->bInterfaceClass == 0x1 &&
intfd->bInterfaceSubClass == 0x2) {
USB_OFFLOAD_INFO("Multiple EP in one intf. NOT SUPPORT!!\n");
return -1;
}
}
}
return 0;
}
int usb_offload_cleanup(void)
{
int ret = 0;
struct usb_audio_stream_msg msg = {0};
unsigned int card_num = uodev->card_num;
USB_OFFLOAD_INFO("%d\n", __LINE__);
uodev->is_streaming = false;
uodev->tx_streaming = false;
uodev->rx_streaming = false;
uodev->adsp_inited = false;
uodev->opened = false;
msg.status = USB_AUDIO_STREAM_REQ_STOP;
msg.status_valid = 1;
/* write to audio ipi*/
ret = send_disconnect_ipi_msg_to_adsp();
/* wait response */
USB_OFFLOAD_INFO("send_disconnect_ipi_msg_to_adsp msg, ret: %d\n", ret);
mutex_lock(&uodev->dev_lock);
uaudio_dev_cleanup(&uadev[card_num]);
USB_OFFLOAD_INFO("uadev[%d].in_use: %d ==> 0\n",
card_num, atomic_read(&uadev[card_num].in_use));
atomic_set(&uadev[card_num].in_use, 0);
mutex_unlock(&uodev->dev_lock);
return ret;
}
static int usb_offload_open(struct inode *ip, struct file *fp)
{
struct xhci_hcd *xhci;
struct usb_device *udev;
struct usb_host_config *config;
int err = 0;
int i, class, vid, pid;
USB_OFFLOAD_INFO("++\n");
mutex_lock(&uodev->dev_lock);
if (!buf_dcbaa || !buf_ctx || !buf_seg) {
USB_OFFLOAD_ERR("USB_OFFLOAD_NOT_READY yet!!!\n");
mutex_unlock(&uodev->dev_lock);
return -1;
}
if (!uodev->connected) {
USB_OFFLOAD_ERR("No UAC Device Connected!!!\n");
mutex_unlock(&uodev->dev_lock);
return -1;
}
if (uodev->opened) {
USB_OFFLOAD_ERR("USB Offload Already Opened!!!\n");
err = usb_offload_cleanup();
if (err)
USB_OFFLOAD_ERR("Unable to notify ADSP.\n");
}
if (uodev->xhci == NULL) {
USB_OFFLOAD_ERR("No 'xhci_host' node, NOT SUPPORT USB Offload!\n");
err = -EINVAL;
goto GET_OF_NODE_FAIL;
}
xhci = uodev->xhci;
for (i = 0; i <= 2; i++) {
if (xhci->devs[i] != NULL)
if (xhci->devs[i]->udev != NULL) {
USB_OFFLOAD_INFO("dev %d bDeviceClass: 0x%x\n",
i, xhci->devs[i]->udev->descriptor.bDeviceClass);
USB_OFFLOAD_INFO("dev %d idVendor: 0x%x\n",
i, xhci->devs[i]->udev->descriptor.idVendor);
USB_OFFLOAD_INFO("dev %d idProduct: 0x%x\n",
i, xhci->devs[i]->udev->descriptor.idProduct);
}
}
if (xhci->devs[2] != NULL) {
USB_OFFLOAD_INFO("Multiple Devices - NOT SUPPORT USB OFFLOAD!!\n");
mutex_unlock(&uodev->dev_lock);
return -1;
}
if (xhci->devs[1] != NULL) {
udev = xhci->devs[1]->udev;
class = udev->descriptor.bDeviceClass;
vid = udev->descriptor.idVendor;
pid = udev->descriptor.idProduct;
USB_OFFLOAD_INFO("Single Device - bDeviceClass: 0x%x, VID: 0x%x, PID: 0x%x\n",
class, vid, pid);
if ((class == 0x00 || class == 0xef) &&
udev->actconfig != NULL &&
udev->actconfig->interface[0] != NULL &&
udev->actconfig->interface[0]->cur_altsetting != NULL) {
config = udev->actconfig;
class = config->interface[0]->cur_altsetting->desc.bInterfaceClass;
USB_OFFLOAD_INFO("Single Device - bInterfaceClass: 0x%x\n", class);
if (class == 0x01) {
if (check_usb_offload_quirk(vid, pid)) {
mutex_unlock(&uodev->dev_lock);
return -1;
}
if (check_is_multiple_ep(config)) {
mutex_unlock(&uodev->dev_lock);
return -1;
}
USB_OFFLOAD_INFO("Single UAC - SUPPORT USB OFFLOAD!!\n");
uodev->opened = true;
mutex_unlock(&uodev->dev_lock);
return 0;
}
}
USB_OFFLOAD_INFO("Single Device - Not UAC. NOT SUPPORT USB OFFLOAD!!\n");
}
GET_OF_NODE_FAIL:
mutex_unlock(&uodev->dev_lock);
return -1;
}
static int usb_offload_release(struct inode *ip, struct file *fp)
{
USB_OFFLOAD_INFO("%d\n", __LINE__);
return usb_offload_cleanup();
}
static long usb_offload_ioctl(struct file *fp,
unsigned int cmd, unsigned long value)
{
long ret = 0;
struct usb_audio_stream_info uainfo;
struct mem_info_xhci *xhci_mem;
enum usb_offload_mem_id mem_id;
switch (cmd) {
case USB_OFFLOAD_INIT_ADSP:
USB_OFFLOAD_INFO("USB_OFFLOAD_INIT_ADSP: %ld\n", value);
if (uodev->adsp_inited && value == 1) {
USB_OFFLOAD_ERR("ADSP ALREADY INITED!!!\n");
ret = -EBUSY;
goto fail;
}
if (!uodev->adsp_inited && value == 0) {
USB_OFFLOAD_ERR("ADSP ALREADY DEINITED!!!\n");
ret = -EBUSY;
goto fail;
}
xhci_mem = kzalloc(sizeof(*xhci_mem), GFP_KERNEL);
if (!xhci_mem) {
USB_OFFLOAD_ERR("Fail to allocate xhci_mem\n");
ret = -ENOMEM;
goto fail;
}
if (value == 1) {
if (uodev->default_use_sram) {
mem_id = USB_OFFLOAD_MEM_SRAM_ID;
xhci_mem->use_sram = uodev->default_use_sram;
xhci_mem->xhci_data_addr = SRAM_ADDR;
xhci_mem->xhci_data_size = SRAM_TOTAL_SIZE;
} else {
mem_id = USB_OFFLOAD_MEM_DRAM_ID;
xhci_mem->use_sram = uodev->default_use_sram;
xhci_mem->xhci_data_addr =
usb_offload_mem_buffer[mem_id].phy_addr;
xhci_mem->xhci_data_size =
usb_offload_mem_buffer[mem_id].size;
}
} else {
xhci_mem->xhci_data_addr = 0;
xhci_mem->xhci_data_size = 0;
}
USB_OFFLOAD_INFO("adsp_exception:%d, adsp_ready:%d\n",
uodev->adsp_exception, uodev->adsp_ready);
ret = send_init_ipi_msg_to_adsp(xhci_mem);
if (ret || (value == 0)) {
uodev->is_streaming = false;
uodev->tx_streaming = false;
uodev->rx_streaming = false;
uodev->adsp_inited = false;
} else
uodev->adsp_inited = true;
kfree(xhci_mem);
break;
case USB_OFFLOAD_ENABLE_STREAM:
case USB_OFFLOAD_DISABLE_STREAM:
USB_OFFLOAD_INFO("%s\n",
(cmd == USB_OFFLOAD_ENABLE_STREAM) ?
"USB_OFFLOAD_ENABLE_STREAM":"USB_OFFLOAD_DISABLE_STREAM");
if (!uodev->adsp_inited) {
USB_OFFLOAD_ERR("ADSP NOT INITED YET!!!\n");
ret = -EFAULT;
goto fail;
}
if (copy_from_user(&uainfo, (void __user *)value, sizeof(uainfo))) {
USB_OFFLOAD_ERR("copy_from_user ERR!!!\n");
ret = -EFAULT;
goto fail;
}
if (!is_uainfo_valid(&uainfo)) {
USB_OFFLOAD_ERR("uainfo invalid!!!\n");
ret = -EFAULT;
goto fail;
}
dump_uainfo(&uainfo);
if (cmd == USB_OFFLOAD_ENABLE_STREAM) {
switch (uainfo.direction) {
case 0:
if (uodev->tx_streaming) {
ret = -EBUSY;
USB_OFFLOAD_ERR("TX Stream Already ENABLE!!!\n");
goto fail;
}
break;
case 1:
if (uodev->rx_streaming) {
USB_OFFLOAD_ERR("RX Stream Already ENABLE!!!\n");
ret = -EBUSY;
goto fail;
}
break;
}
}
if (cmd == USB_OFFLOAD_DISABLE_STREAM) {
switch (uainfo.direction) {
case 0:
if (!uodev->tx_streaming) {
USB_OFFLOAD_ERR("TX Stream Already DISABLE!!!\n");
ret = -EBUSY;
goto fail;
}
break;
case 1:
if (!uodev->rx_streaming) {
USB_OFFLOAD_ERR("RX Stream Already DISABLE!!!\n");
ret = -EBUSY;
goto fail;
}
break;
}
}
ret = usb_offload_enable_stream(&uainfo);
if (cmd == USB_OFFLOAD_ENABLE_STREAM && ret == 0) {
switch (uainfo.direction) {
case 0:
uodev->tx_streaming = true;
break;
case 1:
uodev->rx_streaming = true;
break;
}
}
if (cmd == USB_OFFLOAD_DISABLE_STREAM) {
switch (uainfo.direction) {
case 0:
uodev->tx_streaming = false;
break;
case 1:
uodev->rx_streaming = false;
break;
}
}
uodev->is_streaming = uodev->tx_streaming || uodev->rx_streaming;
break;
}
USB_OFFLOAD_INFO("is_stream:%d, tx_stream:%d, rx_stream:%d, inited:%d, opened:%d\n",
uodev->is_streaming, uodev->tx_streaming,
uodev->rx_streaming, uodev->adsp_inited, uodev->opened);
fail:
USB_OFFLOAD_INFO("ioctl returning, ret: %d\n", ret);
return ret;
}
#if IS_ENABLED(CONFIG_COMPAT)
static long usb_offload_compat_ioctl(struct file *fp,
unsigned int cmd, unsigned long value)
{
USB_OFFLOAD_INFO("in...\n");
return usb_offload_ioctl(fp, cmd, value);
}
#endif
static const char usb_offload_shortname[] = "mtk_usb_offload";
/* file operations for /dev/mtk_usb_offload */
static const struct file_operations usb_offload_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = usb_offload_ioctl,
#if IS_ENABLED(CONFIG_COMPAT)
.compat_ioctl = usb_offload_compat_ioctl,
#endif
.open = usb_offload_open,
.release = usb_offload_release,
};
static struct miscdevice usb_offload_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = usb_offload_shortname,
.fops = &usb_offload_fops,
};
static struct xhci_vendor_ops xhci_mtk_vendor_ops = {
.is_usb_offload_enabled = xhci_mtk_is_usb_offload_enabled,
.alloc_dcbaa = xhci_mtk_alloc_dcbaa,
.free_dcbaa = xhci_mtk_free_dcbaa,
.alloc_container_ctx = xhci_mtk_alloc_container_ctx,
.free_container_ctx = xhci_mtk_free_container_ctx,
.alloc_transfer_ring = xhci_mtk_alloc_transfer_ring,
.free_transfer_ring = xhci_mtk_free_transfer_ring,
.is_streaming = xhci_mtk_is_streaming,
};
int xhci_mtk_ssusb_offload_get_mode(struct device *dev)
{
USB_OFFLOAD_INFO("default_use_sram:%d, current_mem_mode:%d, is_streaming:%d\n",
uodev->default_use_sram, uodev->current_mem_mode, uodev->is_streaming);
if (!uodev->is_streaming)
return SSUSB_OFFLOAD_MODE_NONE;
if (uodev->default_use_sram && uodev->current_mem_mode == SSUSB_OFFLOAD_MODE_S)
return SSUSB_OFFLOAD_MODE_S;
else
return SSUSB_OFFLOAD_MODE_D;
}
static int usb_offload_probe(struct platform_device *pdev)
{
struct device_node *node_xhci_host;
int ret = 0;
uodev = devm_kzalloc(&pdev->dev, sizeof(struct usb_offload_dev),
GFP_KERNEL);
if (!uodev) {
USB_OFFLOAD_ERR("Fail to allocate usb_offload_dev\n");
return -ENOMEM;
}
uodev->dev = &pdev->dev;
if (USB_OFFLOAD_USE_SRAM) {
uodev->default_use_sram = true;
uodev->current_mem_mode = SSUSB_OFFLOAD_MODE_S;
uodev->mem_id = USB_OFFLOAD_MEM_SRAM_ID;
} else {
uodev->default_use_sram = false;
uodev->current_mem_mode = SSUSB_OFFLOAD_MODE_D;
uodev->mem_id = USB_OFFLOAD_MEM_DRAM_ID;
}
uodev->is_streaming = false;
uodev->tx_streaming = false;
uodev->rx_streaming = false;
uodev->adsp_inited = false;
uodev->connected = false;
uodev->opened = false;
uodev->xhci = NULL;
USB_OFFLOAD_INFO("default_use_sram:%d, current_mem_mode:%d, mem_id:%d\n",
uodev->default_use_sram, uodev->current_mem_mode, uodev->mem_id);
node_xhci_host = of_parse_phandle(uodev->dev->of_node, "xhci_host", 0);
if (node_xhci_host) {
ret = mtk_init_usb_offload_sharemem(ADSP_XHCI_MEM_ID, USB_OFFLOAD_MEM_DRAM_ID);
if (ret == -EPROBE_DEFER)
goto INIT_SHAREMEM_FAIL;
if (uodev->default_use_sram)
mtk_init_usb_offload_sharemem(0, USB_OFFLOAD_MEM_SRAM_ID);
ret = mtk_usb_offload_gen_pool_create(MIN_USB_OFFLOAD_SHIFT, -1);
if (ret) {
ret = -ENOMEM;
goto GEN_POOL_FAIL;
}
ret = misc_register(&usb_offload_device);
if (ret) {
USB_OFFLOAD_ERR("Fail to allocate usb_offload_device\n");
ret = -ENOMEM;
goto INIT_MISC_DEV_FAIL;
}
uodev->ssusb_offload_notify = kzalloc(
sizeof(*uodev->ssusb_offload_notify), GFP_KERNEL);
if (!uodev->ssusb_offload_notify) {
USB_OFFLOAD_ERR("Fail to alloc ssusb_offload_notify\n");
ret = -ENOMEM;
goto INIT_OFFLOAD_NOTIFY_FAIL;
}
uodev->ssusb_offload_notify->dev = uodev->dev;
uodev->ssusb_offload_notify->get_mode = xhci_mtk_ssusb_offload_get_mode;
/*ret = ssusb_offload_register(uodev->ssusb_offload_notify);*/
mutex_init(&uodev->dev_lock);
USB_OFFLOAD_INFO("Set XHCI vendor hook ops\n");
platform_set_drvdata(pdev, &xhci_mtk_vendor_ops);
#ifdef CFG_RECOVERY_SUPPORT
adsp_register_notify(&adsp_usb_offload_notifier);
#endif
/*sound_usb_trace_init();*/
} else {
USB_OFFLOAD_ERR("No 'xhci_host' node, NOT support USB_OFFLOAD\n");
ret = -ENODEV;
goto INIT_SHAREMEM_FAIL;
}
return ret;
INIT_OFFLOAD_NOTIFY_FAIL:
misc_deregister(&usb_offload_device);
INIT_MISC_DEV_FAIL:
GEN_POOL_FAIL:
if (uodev->default_use_sram)
iounmap((void *) usb_offload_mem_buffer[USB_OFFLOAD_MEM_SRAM_ID].va_addr);
INIT_SHAREMEM_FAIL:
of_node_put(node_xhci_host);
return ret;
}
static int usb_offload_remove(struct platform_device *pdev)
{
/*int ret;*/
USB_OFFLOAD_INFO("\n");
/*ret = ssusb_offload_unregister(uodev->ssusb_offload_notify->dev);*/
if (uodev->default_use_sram)
iounmap((void *) usb_offload_mem_buffer[USB_OFFLOAD_MEM_SRAM_ID].va_addr);
return 0;
}
static const struct of_device_id usb_offload_of_match[] = {
{.compatible = "mediatek,usb-offload",},
{},
};
MODULE_DEVICE_TABLE(of, usb_offload_of_match);
static struct platform_driver usb_offload_driver = {
.probe = usb_offload_probe,
.remove = usb_offload_remove,
.driver = {
.name = "mtk-usb-offload",
.of_match_table = of_match_ptr(usb_offload_of_match),
},
};
module_platform_driver(usb_offload_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek USB Offload Driver");