#include <linux/of.h>
#include <linux/usb.h>
#include <sound/jack.h>
#include <sound/soc-usb.h>
#include "../usb/card.h"
static DEFINE_MUTEX(ctx_mutex);
static LIST_HEAD(usb_ctx_list);
static struct device_node *snd_soc_find_phandle(struct device *dev)
{
struct device_node *node;
node = of_parse_phandle(dev->of_node, "usb-soc-be", 0);
if (!node)
return ERR_PTR(-ENODEV);
return node;
}
static struct snd_soc_usb *snd_soc_usb_ctx_lookup(struct device_node *node)
{
struct snd_soc_usb *ctx;
if (!node)
return NULL;
list_for_each_entry(ctx, &usb_ctx_list, list) {
if (ctx->component->dev->of_node == node)
return ctx;
}
return NULL;
}
static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev)
{
struct snd_soc_usb *ctx;
struct device_node *node;
node = snd_soc_find_phandle(dev);
if (!IS_ERR(node)) {
ctx = snd_soc_usb_ctx_lookup(node);
of_node_put(node);
} else {
ctx = snd_soc_usb_ctx_lookup(dev->of_node);
}
return ctx ? ctx : NULL;
}
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
int ret;
ret = snd_soc_card_jack_new(component->card, "USB Offload Jack",
SND_JACK_USB, jack);
if (ret < 0) {
dev_err(component->card->dev, "Unable to add USB offload jack: %d\n",
ret);
return ret;
}
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(component->card->dev, "Failed to set jack: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
int direction, enum snd_soc_usb_kctl path,
long *route)
{
struct snd_soc_usb *ctx;
int ret = -ENODEV;
mutex_lock(&ctx_mutex);
ctx = snd_soc_find_usb_ctx(dev);
if (!ctx)
goto exit;
if (ctx->update_offload_route_info)
ret = ctx->update_offload_route_info(ctx->component, card, pcm,
direction, path, route);
exit:
mutex_unlock(&ctx_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_update_offload_route);
void *snd_soc_usb_find_priv_data(struct device *usbdev)
{
struct snd_soc_usb *ctx;
mutex_lock(&ctx_mutex);
ctx = snd_soc_find_usb_ctx(usbdev);
mutex_unlock(&ctx_mutex);
return ctx ? ctx->priv_data : NULL;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
int snd_soc_usb_find_supported_format(int card_idx,
struct snd_pcm_hw_params *params,
int direction)
{
struct snd_usb_stream *as;
as = snd_usb_find_suppported_substream(card_idx, params, direction);
if (!as)
return -EOPNOTSUPP;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_find_supported_format);
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
void *data)
{
struct snd_soc_usb *usb;
usb = kzalloc_obj(*usb);
if (!usb)
return ERR_PTR(-ENOMEM);
usb->component = component;
usb->priv_data = data;
return usb;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port);
void snd_soc_usb_free_port(struct snd_soc_usb *usb)
{
snd_soc_usb_remove_port(usb);
kfree(usb);
}
EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
void snd_soc_usb_add_port(struct snd_soc_usb *usb)
{
mutex_lock(&ctx_mutex);
list_add_tail(&usb->list, &usb_ctx_list);
mutex_unlock(&ctx_mutex);
snd_usb_rediscover_devices();
}
EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
void snd_soc_usb_remove_port(struct snd_soc_usb *usb)
{
struct snd_soc_usb *ctx, *tmp;
mutex_lock(&ctx_mutex);
list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
if (ctx == usb) {
list_del(&ctx->list);
break;
}
}
mutex_unlock(&ctx_mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
{
struct snd_soc_usb *ctx;
if (!usbdev)
return -ENODEV;
mutex_lock(&ctx_mutex);
ctx = snd_soc_find_usb_ctx(usbdev);
if (!ctx)
goto exit;
if (ctx->connection_status_cb)
ctx->connection_status_cb(ctx, sdev, true);
exit:
mutex_unlock(&ctx_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
{
struct snd_soc_usb *ctx;
if (!usbdev)
return -ENODEV;
mutex_lock(&ctx_mutex);
ctx = snd_soc_find_usb_ctx(usbdev);
if (!ctx)
goto exit;
if (ctx->connection_status_cb)
ctx->connection_status_cb(ctx, sdev, false);
exit:
mutex_unlock(&ctx_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SoC USB driver for offloading");