6475 lines
180 KiB
C
Executable File
6475 lines
180 KiB
C
Executable File
/* sound/soc/samsung/abox/abox.c
|
|
*
|
|
* ALSA SoC Audio Layer - Samsung Abox driver
|
|
*
|
|
* Copyright (c) 2016 Samsung Electronics Co. Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
/* #define DEBUG */
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/exynos_iovmm.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/smc.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/shm_ipc.h>
|
|
#include <linux/modem_notifier.h>
|
|
|
|
#include <sound/soc.h>
|
|
#include <sound/soc-dapm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/tlv.h>
|
|
#include <sound/samsung/abox.h>
|
|
#include <sound/samsung/vts.h>
|
|
#include <linux/exynos_iovmm.h>
|
|
|
|
#include <soc/samsung/exynos-pmu.h>
|
|
#ifdef CONFIG_EXYNOS_ITMON
|
|
#include <soc/samsung/exynos-itmon.h>
|
|
#endif
|
|
#include "../../../../drivers/iommu/exynos-iommu.h"
|
|
|
|
#include "abox_util.h"
|
|
#include "abox_dbg.h"
|
|
#include "abox_log.h"
|
|
#include "abox_dump.h"
|
|
#include "abox_gic.h"
|
|
#include "abox_failsafe.h"
|
|
#include "abox_if.h"
|
|
#include "abox_vdma.h"
|
|
#include "abox_effect.h"
|
|
#ifdef CONFIG_SCSC_BT
|
|
#include "abox_bt.h"
|
|
#endif
|
|
#include "abox.h"
|
|
|
|
#undef EMULATOR
|
|
#ifdef EMULATOR
|
|
static void __iomem *pmu_alive;
|
|
static void update_mask_value(void __iomem *sfr,
|
|
unsigned int mask, unsigned int value)
|
|
{
|
|
unsigned int sfr_value = readl(sfr);
|
|
|
|
set_mask_value(sfr_value, mask, value);
|
|
writel(sfr_value, sfr);
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_SOC_EXYNOS8895)
|
|
#define GPIO_MODE_ABOX_SYS_PWR_REG (0x1308)
|
|
#define PAD_RETENTION_ABOX_OPTION (0x3048)
|
|
#define ABOX_MAGIC (0x0814)
|
|
#define ABOX_MAGIC_VALUE (0xAB0CAB0C)
|
|
#define ABOX_CPU_CONFIGURATION (0x2520)
|
|
#define ABOX_CPU_LOCAL_PWR_CFG (0x00000001)
|
|
#define ABOX_CPU_STATUS (0x2524)
|
|
#define ABOX_CPU_STATUS_STATUS_MASK (0x00000001)
|
|
#define ABOX_CPU_STANDBY ABOX_CPU_STATUS
|
|
#define ABOX_CPU_STANDBY_WFE_MASK (0x20000000)
|
|
#define ABOX_CPU_STANDBY_WFI_MASK (0x10000000)
|
|
#define ABOX_CPU_OPTION (0x2528)
|
|
#define ABOX_CPU_OPTION_USE_STANDBYWFE_MASK (0x00020000)
|
|
#define ABOX_CPU_OPTION_USE_STANDBYWFI_MASK (0x00010000)
|
|
#define ABOX_CPU_OPTION_ENABLE_CPU_MASK (0x00008000)
|
|
#elif IS_ENABLED(CONFIG_SOC_EXYNOS9810)
|
|
#define GPIO_MODE_ABOX_SYS_PWR_REG (0x1424)
|
|
#define PAD_RETENTION_ABOX_OPTION (0x4170)
|
|
#define ABOX_MAGIC (0x0814)
|
|
#define ABOX_MAGIC_VALUE (0xAB0CAB0C)
|
|
#define ABOX_CPU_CONFIGURATION (0x415C)
|
|
#define ABOX_CPU_LOCAL_PWR_CFG (0x00000001)
|
|
#define ABOX_CPU_STATUS (0x4160)
|
|
#define ABOX_CPU_STATUS_STATUS_MASK (0x00000001)
|
|
#define ABOX_CPU_STANDBY (0x3804)
|
|
#define ABOX_CPU_STANDBY_WFE_MASK (0x20000000)
|
|
#define ABOX_CPU_STANDBY_WFI_MASK (0x10000000)
|
|
#define ABOX_CPU_OPTION (0x4164)
|
|
#define ABOX_CPU_OPTION_ENABLE_CPU_MASK (0x10000000)
|
|
#elif IS_ENABLED(CONFIG_SOC_EXYNOS9610)
|
|
#define GPIO_MODE_ABOX_SYS_PWR_REG (0x1308)
|
|
#define PAD_RETENTION_ABOX_OPTION (0x3048)
|
|
#define ABOX_MAGIC (0x0814)
|
|
#define ABOX_MAGIC_VALUE (0xAB0CAB0C)
|
|
#define ABOX_CPU_CONFIGURATION (0x2520)
|
|
#define ABOX_CPU_LOCAL_PWR_CFG (0x00000001)
|
|
#define ABOX_CPU_STATUS (0x2524)
|
|
#define ABOX_CPU_STATUS_STATUS_MASK (0x00000001)
|
|
#define ABOX_CPU_STANDBY (0x2524)
|
|
#define ABOX_CPU_STANDBY_WFE_MASK (0x20000000)
|
|
#define ABOX_CPU_STANDBY_WFI_MASK (0x10000000)
|
|
#define ABOX_CPU_OPTION (0x2528)
|
|
#define ABOX_CPU_OPTION_ENABLE_CPU_MASK (0x00008000)
|
|
#endif
|
|
|
|
#define DEFAULT_CPU_GEAR_ID (0xAB0CDEFA)
|
|
#define BOOT_CPU_GEAR_ID (0xB00DB00D)
|
|
#define TEST_CPU_GEAR_ID (DEFAULT_CPU_GEAR_ID + 1)
|
|
#define DEFAULT_LIT_FREQ_ID DEFAULT_CPU_GEAR_ID
|
|
#define DEFAULT_BIG_FREQ_ID DEFAULT_CPU_GEAR_ID
|
|
#define DEFAULT_HMP_BOOST_ID DEFAULT_CPU_GEAR_ID
|
|
#define DEFAULT_INT_FREQ_ID DEFAULT_CPU_GEAR_ID
|
|
#define DEFAULT_MIF_FREQ_ID DEFAULT_CPU_GEAR_ID
|
|
#define AUD_PLL_RATE_KHZ (1179648)
|
|
#define AUD_PLL_RATE_HZ_BYPASS (26000000)
|
|
#define AUDIF_RATE_HZ (24576000)
|
|
#define CALLIOPE_ENABLE_TIMEOUT_MS (1000)
|
|
#define IPC_TIMEOUT_US (10000)
|
|
#define BOOT_DONE_TIMEOUT_MS (10000)
|
|
#define IPC_RETRY (10)
|
|
|
|
#define DMA_VOL_FACTOR_MAX_STEPS (0xFFFFFF)
|
|
|
|
#define ERAP(wname, wcontrols, event_fn, wparams) \
|
|
{ .id = snd_soc_dapm_dai_link, .name = wname, \
|
|
.reg = SND_SOC_NOPM, .event = event_fn, \
|
|
.kcontrol_news = wcontrols, .num_kcontrols = 1, \
|
|
.params = wparams, .num_params = ARRAY_SIZE(wparams), \
|
|
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_WILL_PMD }
|
|
|
|
/* For only external static functions */
|
|
static struct abox_data *p_abox_data;
|
|
|
|
struct abox_data *abox_get_abox_data(void)
|
|
{
|
|
return p_abox_data;
|
|
}
|
|
|
|
static int abox_iommu_fault_handler(
|
|
struct iommu_domain *domain, struct device *dev,
|
|
unsigned long fault_addr, int fault_flags, void *token)
|
|
{
|
|
struct abox_data *data = token;
|
|
|
|
abox_dbg_print_gpr(&data->pdev->dev, data);
|
|
return 0;
|
|
}
|
|
|
|
static void abox_cpu_power(bool on);
|
|
static int abox_cpu_enable(bool enable);
|
|
static int abox_cpu_pm_ipc(struct abox_data *data, bool resume);
|
|
static void abox_boot_done(struct device *dev, unsigned int version);
|
|
static int abox_enable(struct device *dev);
|
|
|
|
static void exynos_abox_panic_handler(void)
|
|
{
|
|
static bool has_run;
|
|
struct abox_data *data = p_abox_data;
|
|
struct device *dev = data ? (data->pdev ? &data->pdev->dev : NULL) :
|
|
NULL;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (abox_is_on() && dev) {
|
|
if (has_run) {
|
|
dev_info(dev, "already dumped\n");
|
|
return;
|
|
}
|
|
has_run = true;
|
|
|
|
abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_KERNEL, "panic");
|
|
abox_cpu_pm_ipc(data, false);
|
|
writel(0x504E4943, data->sram_base + data->sram_size -
|
|
sizeof(u32));
|
|
abox_cpu_enable(false);
|
|
abox_cpu_power(false);
|
|
abox_cpu_power(true);
|
|
abox_cpu_enable(true);
|
|
mdelay(100);
|
|
abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_KERNEL, "panic");
|
|
} else {
|
|
dev_info(dev, "%s: dump is skipped due to no power\n",
|
|
__func__);
|
|
}
|
|
}
|
|
|
|
static int abox_panic_handler(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
exynos_abox_panic_handler();
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block abox_panic_notifier = {
|
|
.notifier_call = abox_panic_handler,
|
|
.next = NULL,
|
|
.priority = 0 /* priority: INT_MAX >= x >= 0 */
|
|
};
|
|
|
|
static struct platform_driver samsung_abox_driver;
|
|
static bool is_abox(struct device *dev)
|
|
{
|
|
return (&samsung_abox_driver.driver) == dev->driver;
|
|
}
|
|
|
|
static void abox_probe_quirks(struct abox_data *data, struct device_node *np)
|
|
{
|
|
#define QUIRKS "quirks"
|
|
#define DEC_MAP(id) {ABOX_QUIRK_STR_##id, ABOX_QUIRK_##id}
|
|
|
|
static const struct {
|
|
const char *str;
|
|
unsigned int bit;
|
|
} map[] = {
|
|
DEC_MAP(TRY_TO_ASRC_OFF),
|
|
DEC_MAP(SHARE_VTS_SRAM),
|
|
DEC_MAP(OFF_ON_SUSPEND),
|
|
DEC_MAP(SCSC_BT),
|
|
DEC_MAP(SCSC_BT_HACK),
|
|
};
|
|
|
|
int i, ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(map); i++) {
|
|
ret = of_property_match_string(np, QUIRKS, map[i].str);
|
|
if (ret >= 0)
|
|
data->quirks |= map[i].bit;
|
|
}
|
|
}
|
|
|
|
|
|
int abox_disable_qchannel(struct device *dev, struct abox_data *data,
|
|
enum qchannel clk, int disable)
|
|
{
|
|
return regmap_update_bits(data->regmap, ABOX_QCHANNEL_DISABLE,
|
|
ABOX_QCHANNEL_DISABLE_MASK(clk),
|
|
!!disable << ABOX_QCHANNEL_DISABLE_L(clk));
|
|
}
|
|
|
|
phys_addr_t abox_addr_to_phys_addr(struct abox_data *data, unsigned int addr)
|
|
{
|
|
phys_addr_t ret;
|
|
|
|
if (addr < IOVA_DRAM_FIRMWARE)
|
|
ret = data->sram_base_phys + addr;
|
|
else
|
|
ret = iommu_iova_to_phys(data->iommu_domain, addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void *abox_dram_addr_to_kernel_addr(struct abox_data *data,
|
|
unsigned long iova)
|
|
{
|
|
struct abox_iommu_mapping *m;
|
|
void *ret = ERR_PTR(-EFAULT);
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(m, &data->iommu_maps, list) {
|
|
if (m->iova <= iova && iova < m->iova + m->bytes) {
|
|
ret = m->area + (iova - m->iova);
|
|
break;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *abox_addr_to_kernel_addr(struct abox_data *data, unsigned int addr)
|
|
{
|
|
void *ret;
|
|
|
|
if (addr < IOVA_DRAM_FIRMWARE)
|
|
ret = data->sram_base + addr;
|
|
else
|
|
ret = abox_dram_addr_to_kernel_addr(data, addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
phys_addr_t abox_iova_to_phys(struct device *dev, unsigned long iova)
|
|
{
|
|
return abox_addr_to_phys_addr(dev_get_drvdata(dev), iova);
|
|
}
|
|
EXPORT_SYMBOL(abox_iova_to_phys);
|
|
|
|
void *abox_iova_to_virt(struct device *dev, unsigned long iova)
|
|
{
|
|
return abox_addr_to_kernel_addr(dev_get_drvdata(dev), iova);
|
|
}
|
|
EXPORT_SYMBOL(abox_iova_to_virt);
|
|
|
|
static int abox_sif_idx(enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return configmsg - ((configmsg < SET_MIXER_FORMAT) ?
|
|
SET_MIXER_SAMPLE_RATE : SET_MIXER_FORMAT);
|
|
}
|
|
|
|
static unsigned int abox_get_sif_rate_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return data->sif_rate_min[abox_sif_idx(configmsg)];
|
|
}
|
|
|
|
static void abox_set_sif_rate_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, unsigned int val)
|
|
{
|
|
data->sif_rate_min[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static snd_pcm_format_t abox_get_sif_format_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return data->sif_format_min[abox_sif_idx(configmsg)];
|
|
}
|
|
|
|
static void abox_set_sif_format_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, snd_pcm_format_t val)
|
|
{
|
|
data->sif_format_min[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static int abox_get_sif_width_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return snd_pcm_format_width(abox_get_sif_format_min(data, configmsg));
|
|
}
|
|
|
|
static void abox_set_sif_width_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, int width)
|
|
{
|
|
struct device *dev = &data->pdev->dev;
|
|
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
|
|
|
|
switch (width) {
|
|
case 16:
|
|
format = SNDRV_PCM_FORMAT_S16;
|
|
break;
|
|
case 24:
|
|
format = SNDRV_PCM_FORMAT_S24;
|
|
break;
|
|
case 32:
|
|
format = SNDRV_PCM_FORMAT_S32;
|
|
break;
|
|
default:
|
|
dev_warn(dev, "%s(%d): invalid argument\n", __func__, width);
|
|
}
|
|
|
|
abox_set_sif_format_min(data, configmsg, format);
|
|
}
|
|
|
|
static unsigned int abox_get_sif_channels_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return data->sif_channels_min[abox_sif_idx(configmsg)];
|
|
}
|
|
|
|
static void __maybe_unused abox_set_sif_channels_min(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, unsigned int val)
|
|
{
|
|
data->sif_channels_min[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static bool abox_get_sif_auto_config(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return data->sif_auto_config[abox_sif_idx(configmsg)];
|
|
}
|
|
|
|
static void abox_set_sif_auto_config(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, bool val)
|
|
{
|
|
data->sif_auto_config[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static unsigned int abox_get_sif_rate(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
unsigned int val = data->sif_rate[abox_sif_idx(configmsg)];
|
|
unsigned int min = abox_get_sif_rate_min(data, configmsg);
|
|
|
|
return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
|
|
min : val;
|
|
}
|
|
|
|
static void abox_set_sif_rate(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, unsigned int val)
|
|
{
|
|
data->sif_rate[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static snd_pcm_format_t abox_get_sif_format(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
snd_pcm_format_t val = data->sif_format[abox_sif_idx(configmsg)];
|
|
snd_pcm_format_t min = abox_get_sif_format_min(data, configmsg);
|
|
|
|
return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
|
|
min : val;
|
|
}
|
|
|
|
static void abox_set_sif_format(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, snd_pcm_format_t val)
|
|
{
|
|
data->sif_format[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static int abox_get_sif_physical_width(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
snd_pcm_format_t format = abox_get_sif_format(data, configmsg);
|
|
|
|
return snd_pcm_format_physical_width(format);
|
|
}
|
|
|
|
static int abox_get_sif_width(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
return snd_pcm_format_width(abox_get_sif_format(data, configmsg));
|
|
}
|
|
|
|
static void abox_set_sif_width(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, int width)
|
|
{
|
|
struct device *dev = &data->pdev->dev;
|
|
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
|
|
|
|
switch (width) {
|
|
case 16:
|
|
format = SNDRV_PCM_FORMAT_S16;
|
|
break;
|
|
case 24:
|
|
format = SNDRV_PCM_FORMAT_S24;
|
|
break;
|
|
case 32:
|
|
format = SNDRV_PCM_FORMAT_S32;
|
|
break;
|
|
default:
|
|
dev_err(dev, "%s(%d): invalid argument\n", __func__, width);
|
|
}
|
|
|
|
abox_set_sif_format(data, configmsg, format);
|
|
}
|
|
|
|
static unsigned int abox_get_sif_channels(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
unsigned int val = data->sif_channels[abox_sif_idx(configmsg)];
|
|
unsigned int min = abox_get_sif_channels_min(data, configmsg);
|
|
|
|
return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
|
|
min : val;
|
|
}
|
|
|
|
static void abox_set_sif_channels(struct abox_data *data,
|
|
enum ABOX_CONFIGMSG configmsg, unsigned int val)
|
|
{
|
|
data->sif_channels[abox_sif_idx(configmsg)] = val;
|
|
}
|
|
|
|
static bool __abox_ipc_queue_empty(struct abox_data *data)
|
|
{
|
|
return (data->ipc_queue_end == data->ipc_queue_start);
|
|
}
|
|
|
|
static bool __abox_ipc_queue_full(struct abox_data *data)
|
|
{
|
|
size_t length = ARRAY_SIZE(data->ipc_queue);
|
|
|
|
return (((data->ipc_queue_end + 1) % length) == data->ipc_queue_start);
|
|
}
|
|
|
|
static int abox_ipc_queue_put(struct abox_data *data, struct device *dev,
|
|
int hw_irq, const void *supplement, size_t size)
|
|
{
|
|
spinlock_t *lock = &data->ipc_queue_lock;
|
|
size_t length = ARRAY_SIZE(data->ipc_queue);
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
spin_lock_irqsave(lock, flags);
|
|
if (!__abox_ipc_queue_full(data)) {
|
|
struct abox_ipc *ipc;
|
|
|
|
ipc = &data->ipc_queue[data->ipc_queue_end];
|
|
ipc->dev = dev;
|
|
ipc->hw_irq = hw_irq;
|
|
ipc->put_time = sched_clock();
|
|
ipc->get_time = 0;
|
|
memcpy(&ipc->msg, supplement, size);
|
|
data->ipc_queue_end = (data->ipc_queue_end + 1) % length;
|
|
|
|
ret = 0;
|
|
} else {
|
|
ret = -EBUSY;
|
|
}
|
|
spin_unlock_irqrestore(lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int abox_ipc_queue_get(struct abox_data *data, struct abox_ipc *ipc)
|
|
{
|
|
spinlock_t *lock = &data->ipc_queue_lock;
|
|
size_t length = ARRAY_SIZE(data->ipc_queue);
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
spin_lock_irqsave(lock, flags);
|
|
if (!__abox_ipc_queue_empty(data)) {
|
|
struct abox_ipc *tmp;
|
|
|
|
tmp = &data->ipc_queue[data->ipc_queue_start];
|
|
tmp->get_time = sched_clock();
|
|
*ipc = *tmp;
|
|
data->ipc_queue_start = (data->ipc_queue_start + 1) % length;
|
|
|
|
ret = 0;
|
|
} else {
|
|
ret = -ENODATA;
|
|
}
|
|
spin_unlock_irqrestore(lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool abox_can_calliope_ipc(struct device *dev,
|
|
struct abox_data *data)
|
|
{
|
|
bool ret = true;
|
|
|
|
switch (data->calliope_state) {
|
|
case CALLIOPE_DISABLING:
|
|
case CALLIOPE_ENABLED:
|
|
break;
|
|
case CALLIOPE_ENABLING:
|
|
wait_event_timeout(data->ipc_wait_queue,
|
|
data->calliope_state == CALLIOPE_ENABLED,
|
|
msecs_to_jiffies(CALLIOPE_ENABLE_TIMEOUT_MS));
|
|
if (data->calliope_state == CALLIOPE_ENABLED)
|
|
break;
|
|
/* Fallthrough */
|
|
case CALLIOPE_DISABLED:
|
|
default:
|
|
dev_warn(dev, "Invalid calliope state: %d\n",
|
|
data->calliope_state);
|
|
ret = false;
|
|
}
|
|
|
|
dev_dbg(dev, "%s: %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __abox_process_ipc(struct device *dev, struct abox_data *data,
|
|
int hw_irq, const ABOX_IPC_MSG *msg)
|
|
{
|
|
static unsigned int tout_cnt;
|
|
static DEFINE_SPINLOCK(lock);
|
|
|
|
void __iomem *tx_base = data->sram_base + data->ipc_tx_offset;
|
|
void __iomem *tx_ack = data->sram_base + data->ipc_tx_ack_offset;
|
|
int ret, i;
|
|
|
|
dev_dbg(dev, "%s(%d, %d, %d)\n", __func__, hw_irq,
|
|
msg->ipcid, msg->msg.system.msgtype);
|
|
|
|
do {
|
|
spin_lock(&lock);
|
|
|
|
memcpy_toio(tx_base, msg, sizeof(*msg));
|
|
writel(1, tx_ack);
|
|
abox_gic_generate_interrupt(data->dev_gic, hw_irq);
|
|
for (i = IPC_TIMEOUT_US; i && readl(tx_ack); i--)
|
|
udelay(1);
|
|
|
|
if (readl(tx_ack)) {
|
|
tout_cnt++;
|
|
dev_warn_ratelimited(dev, "Transaction timeout(%d)\n",
|
|
tout_cnt);
|
|
|
|
if (tout_cnt == 1)
|
|
abox_dbg_dump_simple(dev, data,
|
|
"Transaction timeout");
|
|
|
|
if ((tout_cnt % IPC_RETRY) == 0) {
|
|
abox_failsafe_report(dev);
|
|
writel(0, tx_ack);
|
|
}
|
|
|
|
ret = -EIO;
|
|
} else {
|
|
tout_cnt = 0;
|
|
ret = 0;
|
|
}
|
|
spin_unlock(&lock);
|
|
} while (readl(tx_ack));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void abox_process_ipc(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data, ipc_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
struct abox_ipc ipc;
|
|
|
|
dev_dbg(dev, "%s: %d %d\n", __func__, data->ipc_queue_start,
|
|
data->ipc_queue_end);
|
|
|
|
pm_runtime_get_sync(dev);
|
|
|
|
if (abox_can_calliope_ipc(dev, data)) {
|
|
while (abox_ipc_queue_get(data, &ipc) == 0) {
|
|
struct device *dev = ipc.dev;
|
|
int hw_irq = ipc.hw_irq;
|
|
ABOX_IPC_MSG *msg = &ipc.msg;
|
|
|
|
__abox_process_ipc(dev, data, hw_irq, msg);
|
|
|
|
/* giving time to ABOX for processing */
|
|
usleep_range(10, 100);
|
|
}
|
|
}
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
}
|
|
|
|
static int abox_schedule_ipc(struct device *dev, struct abox_data *data,
|
|
int hw_irq, const void *supplement, size_t size,
|
|
bool atomic, bool sync)
|
|
{
|
|
struct abox_ipc *ipc;
|
|
int retry = 0;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%d, %zu, %d, %d)\n", __func__, hw_irq,
|
|
size, atomic, sync);
|
|
|
|
if (unlikely(sizeof(ipc->msg) < size)) {
|
|
dev_err(dev, "%s: too large supplement\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
do {
|
|
ret = abox_ipc_queue_put(data, dev, hw_irq, supplement, size);
|
|
queue_work(data->ipc_workqueue, &data->ipc_work);
|
|
if (!atomic && sync)
|
|
flush_work(&data->ipc_work);
|
|
if (ret >= 0)
|
|
break;
|
|
|
|
if (!atomic) {
|
|
dev_info(dev, "%s: flush(%d)\n", __func__, retry);
|
|
flush_work(&data->ipc_work);
|
|
} else {
|
|
dev_info(dev, "%s: delay(%d)\n", __func__, retry);
|
|
mdelay(10);
|
|
}
|
|
} while (retry++ < IPC_RETRY);
|
|
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s(%d): ipc queue overflow\n", __func__, hw_irq);
|
|
abox_failsafe_report(dev);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int abox_request_ipc(struct device *dev,
|
|
int hw_irq, const void *supplement,
|
|
size_t size, int atomic, int sync)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
if (atomic && sync) {
|
|
ret = __abox_process_ipc(dev, data, hw_irq, supplement);
|
|
} else {
|
|
ret = abox_schedule_ipc(dev, data, hw_irq, supplement, size,
|
|
!!atomic, !!sync);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(abox_request_ipc);
|
|
|
|
bool abox_is_on(void)
|
|
{
|
|
return p_abox_data && p_abox_data->enabled;
|
|
}
|
|
EXPORT_SYMBOL(abox_is_on);
|
|
|
|
int abox_register_bclk_usage(struct device *dev, struct abox_data *data,
|
|
enum abox_dai dai_id, unsigned int rate, unsigned int channels,
|
|
unsigned int width)
|
|
{
|
|
unsigned long target_pll, audif_rate;
|
|
int id = dai_id - ABOX_UAIF0;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
dev_dbg(dev, "%s(%d, %d)\n", __func__, id, rate);
|
|
|
|
if (id < 0 || id >= ABOX_DAI_COUNT) {
|
|
dev_err(dev, "invalid dai_id: %d\n", dai_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (rate == 0) {
|
|
data->audif_rates[id] = 0;
|
|
return 0;
|
|
}
|
|
|
|
target_pll = ((rate % 44100) == 0) ? AUD_PLL_RATE_HZ_FOR_44100 :
|
|
AUD_PLL_RATE_HZ_FOR_48000;
|
|
if (target_pll != clk_get_rate(data->clk_pll)) {
|
|
dev_info(dev, "Set AUD_PLL rate: %lu -> %lu\n",
|
|
clk_get_rate(data->clk_pll), target_pll);
|
|
ret = clk_set_rate(data->clk_pll, target_pll);
|
|
if (ret < 0) {
|
|
dev_err(dev, "AUD_PLL set error=%d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (data->uaif_max_div <= 32) {
|
|
if ((rate % 44100) == 0)
|
|
audif_rate = ((rate > 176400) ? 352800 : 176400) *
|
|
width * 2;
|
|
else
|
|
audif_rate = ((rate > 192000) ? 384000 : 192000) *
|
|
width * 2;
|
|
|
|
while (audif_rate / rate / channels / width >
|
|
data->uaif_max_div)
|
|
audif_rate /= 2;
|
|
} else {
|
|
int clk_width = 96; /* LCM of 24 and 32 */
|
|
int clk_channels = 2;
|
|
|
|
if ((rate % 44100) == 0)
|
|
audif_rate = 352800 * clk_width * clk_channels;
|
|
else
|
|
audif_rate = 384000 * clk_width * clk_channels;
|
|
|
|
if (audif_rate < rate * width * channels)
|
|
audif_rate = rate * width * channels;
|
|
}
|
|
|
|
data->audif_rates[id] = audif_rate;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(data->audif_rates); i++) {
|
|
if (data->audif_rates[i] > 0 &&
|
|
data->audif_rates[i] > audif_rate) {
|
|
audif_rate = data->audif_rates[i];
|
|
}
|
|
}
|
|
|
|
ret = clk_set_rate(data->clk_audif, audif_rate);
|
|
if (ret < 0)
|
|
dev_err(dev, "Failed to set audif clock: %d\n", ret);
|
|
|
|
dev_info(dev, "audif clock: %lu\n", clk_get_rate(data->clk_audif));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int abox_sif_format_put_ipc(struct device *dev, snd_pcm_format_t format,
|
|
int channels, enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_ABOX_CONFIG_MSG *abox_config_msg = &msg.msg.config;
|
|
int width = snd_pcm_format_width(format);
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%d, %d, %d)\n", __func__, width, channels, configmsg);
|
|
|
|
abox_set_sif_format(data, configmsg, format);
|
|
abox_set_sif_channels(data, configmsg, channels);
|
|
|
|
/* update manually for regmap cache sync */
|
|
switch (configmsg) {
|
|
case SET_MIXER_SAMPLE_RATE:
|
|
case SET_MIXER_FORMAT:
|
|
regmap_update_bits(data->regmap, ABOX_SPUS_CTRL1,
|
|
ABOX_SPUS_MIXP_FORMAT_MASK,
|
|
abox_get_format(width, channels) <<
|
|
ABOX_SPUS_MIXP_FORMAT_L);
|
|
break;
|
|
case SET_RECP_SAMPLE_RATE:
|
|
case SET_RECP_FORMAT:
|
|
regmap_update_bits(data->regmap, ABOX_SPUM_CTRL1,
|
|
ABOX_RECP_SRC_FORMAT_MASK,
|
|
abox_get_format(width, channels) <<
|
|
ABOX_RECP_SRC_FORMAT_L);
|
|
break;
|
|
default:
|
|
/* Nothing to do */
|
|
break;
|
|
}
|
|
|
|
msg.ipcid = IPC_ABOX_CONFIG;
|
|
abox_config_msg->param1 = abox_get_sif_width(data, configmsg);
|
|
abox_config_msg->param2 = abox_get_sif_channels(data, configmsg);
|
|
abox_config_msg->msgtype = configmsg;
|
|
ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
if (ret < 0)
|
|
dev_err(dev, "%d(%d bit, %d channels) failed: %d\n", configmsg,
|
|
width, channels, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int abox_sifsx_cnt_val(unsigned long aclk, unsigned int rate,
|
|
unsigned int physical_width, unsigned int channels)
|
|
{
|
|
static const int correction = -2;
|
|
unsigned int n, d;
|
|
|
|
/* k = n / d */
|
|
d = channels;
|
|
n = 2 * (32 / physical_width);
|
|
|
|
return ((aclk * n) / d / rate) - 5 + correction;
|
|
}
|
|
|
|
static int abox_sifs_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
|
{
|
|
struct snd_soc_pcm_runtime *be = substream->private_data;
|
|
struct snd_soc_dpcm *dpcm;
|
|
int stream = substream->stream;
|
|
struct device *dev = dai->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct regmap *regmap = data->regmap;
|
|
enum abox_dai id = dai->id;
|
|
unsigned int rate = params_rate(params);
|
|
unsigned int width = params_width(params);
|
|
unsigned int pwidth = params_physical_width(params);
|
|
unsigned int channels = params_channels(params);
|
|
unsigned long aclk;
|
|
unsigned int cnt_val;
|
|
bool skip = true;
|
|
int ret = 0;
|
|
|
|
if (stream != SNDRV_PCM_STREAM_CAPTURE)
|
|
goto out;
|
|
|
|
/* sifs count is needed only when SIFS is connected to NSRC */
|
|
list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
|
|
if (dpcm->fe->cpu_dai->id != ABOX_WDMA0) {
|
|
skip = false;
|
|
break;
|
|
}
|
|
}
|
|
if (skip)
|
|
goto out;
|
|
|
|
abox_request_cpu_gear_dai(dev, data, dai, ABOX_CPU_GEAR_MAX);
|
|
abox_cpu_gear_barrier(data);
|
|
|
|
aclk = clk_get_rate(data->clk_bus);
|
|
cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
|
|
|
|
dev_info(dev, "%s[%d](%ubit %uchannel %uHz at %luHz): %u\n",
|
|
__func__, id, width, channels, rate, aclk, cnt_val);
|
|
|
|
switch (id) {
|
|
case ABOX_SIFS0:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS0_CNT_VAL_MASK,
|
|
cnt_val << ABOX_SIFS0_CNT_VAL_L);
|
|
break;
|
|
case ABOX_SIFS1:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS1_CNT_VAL_MASK,
|
|
cnt_val << ABOX_SIFS1_CNT_VAL_L);
|
|
break;
|
|
case ABOX_SIFS2:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
|
|
ABOX_SIFS2_CNT_VAL_MASK,
|
|
cnt_val << ABOX_SIFS2_CNT_VAL_L);
|
|
break;
|
|
default:
|
|
dev_err(dev, "%s: invalid id(%d)\n", __func__, id);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int abox_sifs_hw_free(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct device *dev = dai->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct regmap *regmap = data->regmap;
|
|
enum abox_dai id = dai->id;
|
|
int ret = 0;
|
|
|
|
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
|
|
goto out;
|
|
|
|
dev_info(dev, "%s[%d]\n", __func__, id);
|
|
|
|
switch (id) {
|
|
case ABOX_SIFS0:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS0_CNT_VAL_MASK, 0);
|
|
break;
|
|
case ABOX_SIFS1:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS1_CNT_VAL_MASK, 0);
|
|
break;
|
|
case ABOX_SIFS2:
|
|
ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
|
|
ABOX_SIFS2_CNT_VAL_MASK, 0);
|
|
break;
|
|
default:
|
|
dev_err(dev, "%s: invalid id(%d)\n", __func__, id);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
abox_request_cpu_gear_dai(dev, data, dai, ABOX_CPU_GEAR_MIN);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops abox_sifs_dai_ops = {
|
|
.hw_params = abox_sifs_hw_params,
|
|
.hw_free = abox_sifs_hw_free,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver abox_dais[] = {
|
|
{
|
|
.name = "RDMA0",
|
|
.id = ABOX_RDMA0,
|
|
.playback = {
|
|
.stream_name = "RDMA0 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA1",
|
|
.id = ABOX_RDMA1,
|
|
.playback = {
|
|
.stream_name = "RDMA1 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA2",
|
|
.id = ABOX_RDMA2,
|
|
.playback = {
|
|
.stream_name = "RDMA2 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA3",
|
|
.id = ABOX_RDMA3,
|
|
.playback = {
|
|
.stream_name = "RDMA3 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA4",
|
|
.id = ABOX_RDMA4,
|
|
.playback = {
|
|
.stream_name = "RDMA4 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA5",
|
|
.id = ABOX_RDMA5,
|
|
.playback = {
|
|
.stream_name = "RDMA5 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.compress_new = snd_soc_new_compress,
|
|
},
|
|
{
|
|
.name = "RDMA6",
|
|
.id = ABOX_RDMA6,
|
|
.playback = {
|
|
.stream_name = "RDMA6 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "RDMA7",
|
|
.id = ABOX_RDMA7,
|
|
.playback = {
|
|
.stream_name = "RDMA7 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "WDMA0",
|
|
.id = ABOX_WDMA0,
|
|
.capture = {
|
|
.stream_name = "WDMA0 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_WDMA_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "WDMA1",
|
|
.id = ABOX_WDMA1,
|
|
.capture = {
|
|
.stream_name = "WDMA1 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_WDMA_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "WDMA2",
|
|
.id = ABOX_WDMA2,
|
|
.capture = {
|
|
.stream_name = "WDMA2 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_WDMA_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "WDMA3",
|
|
.id = ABOX_WDMA3,
|
|
.capture = {
|
|
.stream_name = "WDMA3 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_WDMA_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "WDMA4",
|
|
.id = ABOX_WDMA4,
|
|
.capture = {
|
|
.stream_name = "WDMA4 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_WDMA_SAMPLE_FORMATS,
|
|
},
|
|
},
|
|
{
|
|
.name = "SIFS0",
|
|
.id = ABOX_SIFS0,
|
|
.playback = {
|
|
.stream_name = "SIFS0 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "SIFS0 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.ops = &abox_sifs_dai_ops,
|
|
},
|
|
{
|
|
.name = "SIFS1",
|
|
.id = ABOX_SIFS1,
|
|
.playback = {
|
|
.stream_name = "SIFS1 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "SIFS1 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.ops = &abox_sifs_dai_ops,
|
|
},
|
|
{
|
|
.name = "SIFS2",
|
|
.id = ABOX_SIFS2,
|
|
.playback = {
|
|
.stream_name = "SIFS2 Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "SIFS2 Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = ABOX_SAMPLING_RATES,
|
|
.rate_min = 8000,
|
|
.rate_max = 384000,
|
|
.formats = ABOX_SAMPLE_FORMATS,
|
|
},
|
|
.ops = &abox_sifs_dai_ops,
|
|
},
|
|
};
|
|
|
|
static int abox_cmpnt_probe(struct snd_soc_component *component)
|
|
{
|
|
struct device *dev = component->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
data->cmpnt = component;
|
|
snd_soc_component_update_bits(component, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_RSRC_RECP_MASK,
|
|
ABOX_FUNC_CHAIN_RSRC_RECP_MASK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_cmpnt_remove(struct snd_soc_component *component)
|
|
{
|
|
struct device *dev = component->dev;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
snd_soc_component_exit_regmap(component);
|
|
}
|
|
|
|
static int abox_sample_rate_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = abox_get_sif_rate(data, reg);
|
|
|
|
dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_sample_rate_put_ipc(struct device *dev, unsigned int val,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_ABOX_CONFIG_MSG *abox_config_msg = &msg.msg.config;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%u, 0x%08x)\n", __func__, val, configmsg);
|
|
|
|
abox_set_sif_rate(data, configmsg, val);
|
|
|
|
msg.ipcid = IPC_ABOX_CONFIG;
|
|
abox_config_msg->param1 = abox_get_sif_rate(data, configmsg);
|
|
abox_config_msg->msgtype = configmsg;
|
|
ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s(%u, 0x%08x) failed: %d\n", __func__, val,
|
|
configmsg, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int abox_sample_rate_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
|
|
|
|
return abox_sample_rate_put_ipc(dev, val, reg);
|
|
}
|
|
|
|
static int abox_bit_width_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = abox_get_sif_width(data, reg);
|
|
|
|
dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_bit_width_put_ipc(struct device *dev, unsigned int val,
|
|
enum ABOX_CONFIGMSG configmsg)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
|
|
int channels = data->sif_channels[abox_sif_idx(configmsg)];
|
|
|
|
dev_dbg(dev, "%s(%u, 0x%08x)\n", __func__, val, configmsg);
|
|
|
|
switch (val) {
|
|
case 16:
|
|
format = SNDRV_PCM_FORMAT_S16;
|
|
break;
|
|
case 24:
|
|
format = SNDRV_PCM_FORMAT_S24;
|
|
break;
|
|
case 32:
|
|
format = SNDRV_PCM_FORMAT_S32;
|
|
break;
|
|
default:
|
|
dev_warn(dev, "%s(%u, 0x%08x) invalid argument\n", __func__,
|
|
val, configmsg);
|
|
break;
|
|
}
|
|
|
|
return abox_sif_format_put_ipc(dev, format, channels, configmsg);
|
|
}
|
|
|
|
static int abox_bit_width_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
|
|
|
|
return abox_bit_width_put_ipc(dev, val, reg);
|
|
}
|
|
|
|
static int abox_sample_rate_min_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = abox_get_sif_rate_min(data, reg);
|
|
|
|
dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_sample_rate_min_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
|
|
|
|
abox_set_sif_rate_min(data, reg, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_bit_width_min_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = abox_get_sif_width_min(data, reg);
|
|
|
|
dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_bit_width_min_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
|
|
|
|
abox_set_sif_width_min(data, reg, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_auto_config_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = abox_get_sif_auto_config(data, reg);
|
|
|
|
dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_auto_config_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int reg = mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
|
|
|
|
abox_set_sif_auto_config(data, reg, !!val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_erap_handler_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
enum ABOX_ERAP_TYPE type = (enum ABOX_ERAP_TYPE)mc->reg;
|
|
|
|
dev_dbg(dev, "%s(%d)\n", __func__, type);
|
|
|
|
ucontrol->value.integer.value[0] = data->erap_status[type];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_erap_handler_put_ipc(struct device *dev,
|
|
enum ABOX_ERAP_TYPE type, unsigned int val)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
|
|
struct ERAP_ONOFF_PARAM *erap_param = &erap_msg->param.onoff;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%u, %d)\n", __func__, val, type);
|
|
|
|
msg.ipcid = IPC_ERAP;
|
|
erap_msg->msgtype = val ? REALTIME_OPEN : REALTIME_CLOSE;
|
|
erap_param->type = type;
|
|
erap_param->channel_no = 0;
|
|
erap_param->version = val;
|
|
ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
if (ret < 0)
|
|
dev_err(dev, "erap control failed(type:%d, status:%d)\n",
|
|
type, val);
|
|
|
|
data->erap_status[type] = val;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int abox_erap_handler_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
enum ABOX_ERAP_TYPE type = (enum ABOX_ERAP_TYPE)mc->reg;
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(%u, %d)\n", __func__, val, type);
|
|
|
|
return abox_erap_handler_put_ipc(dev, type, val);
|
|
}
|
|
|
|
static int abox_audio_mode_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int item;
|
|
|
|
dev_dbg(dev, "%s: %u\n", __func__, data->audio_mode);
|
|
|
|
item = snd_soc_enum_val_to_item(e, data->audio_mode);
|
|
ucontrol->value.enumerated.item[0] = item;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_audio_mode_put_ipc(struct device *dev, enum audio_mode mode)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
|
|
dev_dbg(dev, "%s(%d)\n", __func__, mode);
|
|
|
|
data->audio_mode_time = local_clock();
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_SET_MODE;
|
|
system_msg->param1 = data->audio_mode = mode;
|
|
return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
}
|
|
|
|
static int abox_audio_mode_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int *item = ucontrol->value.enumerated.item;
|
|
enum audio_mode mode;
|
|
|
|
if (item[0] >= e->items)
|
|
return -EINVAL;
|
|
|
|
mode = snd_soc_enum_item_to_val(e, item[0]);
|
|
dev_info(dev, "%s(%u)\n", __func__, mode);
|
|
|
|
return abox_audio_mode_put_ipc(dev, mode);
|
|
}
|
|
|
|
static const char * const abox_audio_mode_enum_texts[] = {
|
|
"NORMAL",
|
|
"RINGTONE",
|
|
"IN_CALL",
|
|
"IN_COMMUNICATION",
|
|
"IN_VIDEOCALL",
|
|
};
|
|
static const unsigned int abox_audio_mode_enum_values[] = {
|
|
MODE_NORMAL,
|
|
MODE_RINGTONE,
|
|
MODE_IN_CALL,
|
|
MODE_IN_COMMUNICATION,
|
|
MODE_IN_VIDEOCALL,
|
|
};
|
|
SOC_VALUE_ENUM_SINGLE_DECL(abox_audio_mode_enum, SND_SOC_NOPM, 0, 0,
|
|
abox_audio_mode_enum_texts, abox_audio_mode_enum_values);
|
|
|
|
static int abox_sound_type_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int item;
|
|
|
|
dev_dbg(dev, "%s: %u\n", __func__, data->sound_type);
|
|
|
|
item = snd_soc_enum_val_to_item(e, data->sound_type);
|
|
ucontrol->value.enumerated.item[0] = item;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_sound_type_put_ipc(struct device *dev, enum sound_type type)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
|
|
dev_dbg(dev, "%s(%d)\n", __func__, type);
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_SET_TYPE;
|
|
system_msg->param1 = data->sound_type = type;
|
|
|
|
return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
}
|
|
|
|
static int abox_sound_type_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int *item = ucontrol->value.enumerated.item;
|
|
enum sound_type type;
|
|
|
|
if (item[0] >= e->items)
|
|
return -EINVAL;
|
|
|
|
type = snd_soc_enum_item_to_val(e, item[0]);
|
|
dev_info(dev, "%s(%d)\n", __func__, type);
|
|
|
|
return abox_sound_type_put_ipc(dev, type);
|
|
}
|
|
static const char * const abox_sound_type_enum_texts[] = {
|
|
"VOICE",
|
|
"SPEAKER",
|
|
"HEADSET",
|
|
"BTVOICE",
|
|
"USB",
|
|
};
|
|
static const unsigned int abox_sound_type_enum_values[] = {
|
|
SOUND_TYPE_VOICE,
|
|
SOUND_TYPE_SPEAKER,
|
|
SOUND_TYPE_HEADSET,
|
|
SOUND_TYPE_BTVOICE,
|
|
SOUND_TYPE_USB,
|
|
};
|
|
SOC_VALUE_ENUM_SINGLE_DECL(abox_sound_type_enum, SND_SOC_NOPM, 0, 0,
|
|
abox_sound_type_enum_texts, abox_sound_type_enum_values);
|
|
|
|
static int abox_tickle_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
ucontrol->value.integer.value[0] = data->enabled;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void abox_tickle_work_func(struct work_struct *work)
|
|
{
|
|
struct delayed_work *dwork = to_delayed_work(work);
|
|
struct abox_data *data = container_of(dwork, struct abox_data,
|
|
tickle_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
pm_request_idle(dev);
|
|
}
|
|
static DECLARE_DELAYED_WORK(abox_tickle_work, abox_tickle_work_func);
|
|
|
|
static int abox_tickle_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
long val = ucontrol->value.integer.value[0];
|
|
|
|
dev_dbg(dev, "%s(%ld)\n", __func__, val);
|
|
|
|
if (!!val) {
|
|
pm_request_resume(dev);
|
|
schedule_delayed_work(&data->tickle_work, 1 * HZ);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wake_lock_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
unsigned int val = data->ws.active;
|
|
|
|
dev_dbg(dev, "%s: %u\n", __func__, val);
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wake_lock_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
|
|
|
|
dev_info(dev, "%s(%u)\n", __func__, val);
|
|
|
|
if (val)
|
|
__pm_stay_awake(&data->ws);
|
|
else
|
|
__pm_relax(&data->ws);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_rdma_vol_factor_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
int id = (int)mc->reg;
|
|
unsigned int volumes;
|
|
unsigned int value = 0;
|
|
int ret = 0;
|
|
|
|
ret = regmap_read(data->regmap,
|
|
ABOX_RDMA_VOL_FACTOR(id), &value);
|
|
if (ret < 0) {
|
|
dev_err(dev, "sfr access failed: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
volumes = (value & ABOX_RDMA_VOL_FACTOR_MASK);
|
|
|
|
dev_dbg(dev, "%s(0x%08x, %u)\n", __func__, id, volumes);
|
|
|
|
ucontrol->value.integer.value[0] = volumes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_rdma_vol_factor_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
int id = (int)mc->reg;
|
|
unsigned int volumes;
|
|
unsigned int value = 0;
|
|
int ret = 0;
|
|
|
|
volumes = (unsigned int)ucontrol->value.integer.value[0];
|
|
dev_dbg(dev, "%s[%d]: %u\n", __func__, id, volumes);
|
|
|
|
ret = regmap_read(data->regmap,
|
|
ABOX_RDMA_VOL_FACTOR(id), &value);
|
|
if (ret < 0) {
|
|
dev_err(dev, "sfr access failed: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
set_value_by_name(value, ABOX_RDMA_VOL_FACTOR, volumes);
|
|
|
|
ret = regmap_write(data->regmap,
|
|
ABOX_RDMA_VOL_FACTOR(id), value);
|
|
if (ret < 0) {
|
|
dev_err(dev, "sfr access failed: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const DECLARE_TLV_DB_LINEAR(abox_rdma_vol_factor_gain, 0,
|
|
DMA_VOL_FACTOR_MAX_STEPS);
|
|
|
|
static const struct snd_kcontrol_new abox_cmpnt_controls[] = {
|
|
SOC_SINGLE_EXT("Sampling Rate Mixer", SET_MIXER_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Out1", SET_OUT1_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Out2", SET_OUT2_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Recp", SET_RECP_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux0", SET_INMUX0_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux1", SET_INMUX1_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux2", SET_INMUX2_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux3", SET_INMUX3_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux4", SET_INMUX4_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_get, abox_sample_rate_put),
|
|
SOC_SINGLE_EXT("Bit Width Mixer", SET_MIXER_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Out1", SET_OUT1_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Out2", SET_OUT2_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Recp", SET_RECP_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux0", SET_INMUX0_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux1", SET_INMUX1_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux2", SET_INMUX2_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux3", SET_INMUX3_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux4", SET_INMUX4_FORMAT, 16, 32, 0,
|
|
abox_bit_width_get, abox_bit_width_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Mixer Min", SET_MIXER_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Out1 Min", SET_OUT1_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Out2 Min", SET_OUT2_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Recp Min", SET_RECP_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux0 Min", SET_INMUX0_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux1 Min", SET_INMUX1_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux2 Min", SET_INMUX2_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux3 Min", SET_INMUX3_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Sampling Rate Inmux4 Min", SET_INMUX4_SAMPLE_RATE,
|
|
8000, 384000, 0,
|
|
abox_sample_rate_min_get, abox_sample_rate_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Mixer Min", SET_MIXER_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Out1 Min", SET_OUT1_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Out2 Min", SET_OUT2_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Recp Min", SET_RECP_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux0 Min", SET_INMUX0_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux1 Min", SET_INMUX1_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux2 Min", SET_INMUX2_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux3 Min", SET_INMUX3_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Bit Width Inmux4 Min", SET_INMUX4_FORMAT, 16, 32, 0,
|
|
abox_bit_width_min_get, abox_bit_width_min_put),
|
|
SOC_SINGLE_EXT("Auto Config Mixer", SET_MIXER_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Out1", SET_OUT1_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Out2", SET_OUT2_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Recp", SET_RECP_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Inmux0", SET_INMUX0_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Inmux1", SET_INMUX1_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Inmux2", SET_INMUX2_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Inmux3", SET_INMUX3_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Auto Config Inmux4", SET_INMUX4_SAMPLE_RATE, 0, 1, 0,
|
|
abox_auto_config_get, abox_auto_config_put),
|
|
SOC_SINGLE_EXT("Echo Cancellation", ERAP_ECHO_CANCEL, 0, 2, 0,
|
|
abox_erap_handler_get, abox_erap_handler_put),
|
|
SOC_SINGLE_EXT("VI Sensing", ERAP_VI_SENSE, 0, 2, 0,
|
|
abox_erap_handler_get, abox_erap_handler_put),
|
|
SOC_VALUE_ENUM_EXT("Audio Mode", abox_audio_mode_enum,
|
|
abox_audio_mode_get, abox_audio_mode_put),
|
|
SOC_VALUE_ENUM_EXT("Sound Type", abox_sound_type_enum,
|
|
abox_sound_type_get, abox_sound_type_put),
|
|
SOC_SINGLE_EXT("Tickle", 0, 0, 1, 0, abox_tickle_get, abox_tickle_put),
|
|
SOC_SINGLE_EXT("Wakelock", 0, 0, 1, 0,
|
|
wake_lock_get, wake_lock_put),
|
|
SOC_SINGLE_EXT_TLV("RDMA VOL FACTOR3", 3, 0,
|
|
DMA_VOL_FACTOR_MAX_STEPS, 0,
|
|
abox_rdma_vol_factor_get, abox_rdma_vol_factor_put,
|
|
abox_rdma_vol_factor_gain),
|
|
};
|
|
|
|
static const char * const spus_inx_texts[] = {"RDMA", "SIFSM"};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in0_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(0), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in0_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in1_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(1), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in1_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in2_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(2), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in2_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in3_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(3), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in3_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in3_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in4_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(4), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in4_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in4_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in5_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(5), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in5_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in5_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in6_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(6), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in6_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in6_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_in7_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_IN_L(7), spus_inx_texts);
|
|
static const struct snd_kcontrol_new spus_in7_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spus_in7_enum),
|
|
};
|
|
|
|
static const char * const spus_asrcx_texts[] = {"Off", "On"};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc0_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(0), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc0_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc1_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(1), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc1_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc2_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(2), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc2_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc3_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(3), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc3_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc3_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc4_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(4), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc4_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc4_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc5_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(5), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc5_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc5_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc6_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(6), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc6_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc6_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_asrc7_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_ASRC_L(7), spus_asrcx_texts);
|
|
static const struct snd_kcontrol_new spus_asrc7_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spus_asrc7_enum),
|
|
};
|
|
|
|
static const char * const spus_outx_texts[] = {"SIFS1", "SIFS0", "SIFS2"};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out0_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(0), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out0_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out1_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(1), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out1_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out2_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(2), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out2_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out3_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(3), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out3_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out3_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out4_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(4), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out4_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out4_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out5_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(5), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out5_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out5_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out6_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(6), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out6_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out6_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spus_out7_enum, ABOX_SPUS_CTRL0,
|
|
ABOX_FUNC_CHAIN_SRC_OUT_L(7), spus_outx_texts);
|
|
static const struct snd_kcontrol_new spus_out7_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", spus_out7_enum),
|
|
};
|
|
|
|
static const char * const spusm_texts[] = {
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"UAIF0", "UAIF1", "UAIF2", "UAIF3", "UAIF4",
|
|
"RESERVED", "RESERVED", "SPDY",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spusm_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_SPUSM_L,
|
|
spusm_texts);
|
|
static const struct snd_kcontrol_new spusm_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", spusm_enum),
|
|
};
|
|
|
|
static int abox_flush_mixp(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL2,
|
|
ABOX_SPUS_MIXP_FLUSH_MASK,
|
|
1 << ABOX_SPUS_MIXP_FLUSH_L);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifm(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
|
|
ABOX_SPUS_SIFM_FLUSH_MASK,
|
|
1 << ABOX_SPUS_SIFM_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifs1(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
|
|
ABOX_SPUS_SIFS1_FLUSH_MASK,
|
|
1 << ABOX_SPUS_SIFS1_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifs2(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
|
|
ABOX_SPUS_SIFS2_FLUSH_MASK,
|
|
1 << ABOX_SPUS_SIFS2_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_recp(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL2,
|
|
ABOX_SPUM_RECP_FLUSH_MASK,
|
|
1 << ABOX_SPUM_RECP_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifm0(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
|
|
ABOX_SPUM_SIFM0_FLUSH_MASK,
|
|
1 << ABOX_SPUM_SIFM0_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifm1(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
|
|
ABOX_SPUM_SIFM1_FLUSH_MASK,
|
|
1 << ABOX_SPUM_SIFM1_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifm2(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
|
|
ABOX_SPUM_SIFM2_FLUSH_MASK,
|
|
1 << ABOX_SPUM_SIFM2_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_flush_sifm3(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct device *dev = cmpnt->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
|
|
dev_info(dev, "%s: flush\n", __func__);
|
|
snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
|
|
ABOX_SPUM_SIFM3_FLUSH_MASK,
|
|
1 << ABOX_SPUM_SIFM3_FLUSH_L);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char * const sifsx_texts[] = {
|
|
"SPUS OUT0", "SPUS OUT1", "SPUS OUT2", "SPUS OUT3",
|
|
"SPUS OUT4", "SPUS OUT5", "SPUS OUT6", "SPUS OUT7",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifs1_enum, ABOX_SPUS_CTRL1, ABOX_SIFS_OUT1_SEL_L,
|
|
sifsx_texts);
|
|
static const struct snd_kcontrol_new sifs1_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", sifs1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifs2_enum, ABOX_SPUS_CTRL1, ABOX_SIFS_OUT2_SEL_L,
|
|
sifsx_texts);
|
|
static const struct snd_kcontrol_new sifs2_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", sifs2_enum),
|
|
};
|
|
|
|
static const char * const sifsm_texts[] = {
|
|
"SPUS IN0", "SPUS IN1", "SPUS IN2", "SPUS IN3",
|
|
"SPUS IN4", "SPUS IN5", "SPUS IN6", "SPUS IN7",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifsm_enum, ABOX_SPUS_CTRL1, ABOX_SIFM_IN_SEL_L,
|
|
sifsm_texts);
|
|
static const struct snd_kcontrol_new sifsm_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", sifsm_enum),
|
|
};
|
|
|
|
static const char * const uaif_spkx_texts[] = {
|
|
"RESERVED", "SIFS0", "SIFS1", "SIFS2",
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"SIFMS",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(uaif0_spk_enum, ABOX_ROUTE_CTRL0,
|
|
ABOX_ROUTE_UAIF_SPK_L(0), uaif_spkx_texts);
|
|
static const struct snd_kcontrol_new uaif0_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", uaif0_spk_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(uaif1_spk_enum, ABOX_ROUTE_CTRL0,
|
|
ABOX_ROUTE_UAIF_SPK_L(1), uaif_spkx_texts);
|
|
static const struct snd_kcontrol_new uaif1_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", uaif1_spk_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(uaif2_spk_enum, ABOX_ROUTE_CTRL0,
|
|
ABOX_ROUTE_UAIF_SPK_L(2), uaif_spkx_texts);
|
|
static const struct snd_kcontrol_new uaif2_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", uaif2_spk_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(uaif3_spk_enum, ABOX_ROUTE_CTRL0,
|
|
ABOX_ROUTE_UAIF_SPK_L(3), uaif_spkx_texts);
|
|
static const struct snd_kcontrol_new uaif3_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", uaif3_spk_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(uaif4_spk_enum, ABOX_ROUTE_CTRL0,
|
|
ABOX_ROUTE_UAIF_SPK_L(4), uaif_spkx_texts);
|
|
static const struct snd_kcontrol_new uaif4_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", uaif4_spk_enum),
|
|
};
|
|
|
|
static const char * const dsif_spk_texts[] = {
|
|
"RESERVED", "RESERVED", "SIFS1", "SIFS2",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(dsif_spk_enum, ABOX_ROUTE_CTRL0, 20,
|
|
dsif_spk_texts);
|
|
static const struct snd_kcontrol_new dsif_spk_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", dsif_spk_enum),
|
|
};
|
|
|
|
static const char * const rsrcx_texts[] = {
|
|
"RESERVED", "SIFS0", "SIFS1", "SIFS2",
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"NSRC0", "NSRC1", "NSRC2", "NSRC3",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(rsrc0_enum, ABOX_ROUTE_CTRL2, ABOX_ROUTE_RSRC_L(0),
|
|
rsrcx_texts);
|
|
static const struct snd_kcontrol_new rsrc0_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", rsrc0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(rsrc1_enum, ABOX_ROUTE_CTRL2, ABOX_ROUTE_RSRC_L(1),
|
|
rsrcx_texts);
|
|
static const struct snd_kcontrol_new rsrc1_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", rsrc1_enum),
|
|
};
|
|
|
|
static const char * const nsrcx_texts[] = {
|
|
"RESERVED", "SIFS0", "SIFS1", "SIFS2",
|
|
"RESERVED", "RESERVED", "RESERVED", "RESERVED",
|
|
"UAIF0", "UAIF1", "UAIF2", "UAIF3", "UAIF4",
|
|
"RESERVED", "RESERVED", "SPDY",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(nsrc0_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(0),
|
|
nsrcx_texts);
|
|
static const struct snd_kcontrol_new nsrc0_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", nsrc0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(nsrc1_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(1),
|
|
nsrcx_texts);
|
|
static const struct snd_kcontrol_new nsrc1_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", nsrc1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(nsrc2_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(2),
|
|
nsrcx_texts);
|
|
static const struct snd_kcontrol_new nsrc2_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", nsrc2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(nsrc3_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(3),
|
|
nsrcx_texts);
|
|
static const struct snd_kcontrol_new nsrc3_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", nsrc3_enum),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new recp_controls[] = {
|
|
SOC_DAPM_SINGLE("PIFS0", ABOX_SPUM_CTRL1, ABOX_RECP_SRC_VALID_L, 1, 0),
|
|
SOC_DAPM_SINGLE("PIFS1", ABOX_SPUM_CTRL1, ABOX_RECP_SRC_VALID_H, 1, 0),
|
|
};
|
|
|
|
static const char * const spum_asrcx_texts[] = {"Off", "On"};
|
|
static SOC_ENUM_SINGLE_DECL(spum_asrc0_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_RSRC_ASRC_L, spum_asrcx_texts);
|
|
static const struct snd_kcontrol_new spum_asrc0_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spum_asrc0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spum_asrc1_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_ASRC_L(0), spum_asrcx_texts);
|
|
static const struct snd_kcontrol_new spum_asrc1_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spum_asrc1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spum_asrc2_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_ASRC_L(1), spum_asrcx_texts);
|
|
static const struct snd_kcontrol_new spum_asrc2_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spum_asrc2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(spum_asrc3_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_ASRC_L(2), spum_asrcx_texts);
|
|
static const struct snd_kcontrol_new spum_asrc3_controls[] = {
|
|
SOC_DAPM_ENUM("ASRC", spum_asrc3_enum),
|
|
};
|
|
|
|
static const char * const sifmx_texts[] = {
|
|
"WDMA", "SIFMS",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifm0_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_OUT_L(0), sifmx_texts);
|
|
static const struct snd_kcontrol_new sifm0_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", sifm0_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifm1_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_OUT_L(1), sifmx_texts);
|
|
static const struct snd_kcontrol_new sifm1_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", sifm1_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifm2_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_OUT_L(2), sifmx_texts);
|
|
static const struct snd_kcontrol_new sifm2_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", sifm2_enum),
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifm3_enum, ABOX_SPUM_CTRL0,
|
|
ABOX_FUNC_CHAIN_NSRC_OUT_L(3), sifmx_texts);
|
|
static const struct snd_kcontrol_new sifm3_controls[] = {
|
|
SOC_DAPM_ENUM("DEMUX", sifm3_enum),
|
|
};
|
|
|
|
static const char * const sifms_texts[] = {
|
|
"RESERVED", "SIFM0", "SIFM1", "SIFM2", "SIFM3",
|
|
};
|
|
static SOC_ENUM_SINGLE_DECL(sifms_enum, ABOX_SPUM_CTRL1, ABOX_SIFS_OUT_SEL_L,
|
|
sifms_texts);
|
|
static const struct snd_kcontrol_new sifms_controls[] = {
|
|
SOC_DAPM_ENUM("MUX", sifms_enum),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget abox_cmpnt_dapm_widgets[] = {
|
|
SND_SOC_DAPM_MUX("SPUSM", SND_SOC_NOPM, 0, 0, spusm_controls),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN3", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN4", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN5", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN6", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFSM-SPUS IN7", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_DEMUX_E("SIFSM", SND_SOC_NOPM, 0, 0, sifsm_controls,
|
|
abox_flush_sifm,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_MUX("SPUS IN0", SND_SOC_NOPM, 0, 0, spus_in0_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN1", SND_SOC_NOPM, 0, 0, spus_in1_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN2", SND_SOC_NOPM, 0, 0, spus_in2_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN3", SND_SOC_NOPM, 0, 0, spus_in3_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN4", SND_SOC_NOPM, 0, 0, spus_in4_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN5", SND_SOC_NOPM, 0, 0, spus_in5_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN6", SND_SOC_NOPM, 0, 0, spus_in6_controls),
|
|
SND_SOC_DAPM_MUX("SPUS IN7", SND_SOC_NOPM, 0, 0, spus_in7_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC0", SND_SOC_NOPM, 0, 0, spus_asrc0_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC1", SND_SOC_NOPM, 0, 0, spus_asrc1_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC2", SND_SOC_NOPM, 0, 0, spus_asrc2_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC3", SND_SOC_NOPM, 0, 0, spus_asrc3_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC4", SND_SOC_NOPM, 0, 0, spus_asrc4_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC5", SND_SOC_NOPM, 0, 0, spus_asrc5_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC6", SND_SOC_NOPM, 0, 0, spus_asrc6_controls),
|
|
SND_SOC_DAPM_MUX("SPUS ASRC7", SND_SOC_NOPM, 0, 0, spus_asrc7_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT0", SND_SOC_NOPM, 0, 0, spus_out0_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT1", SND_SOC_NOPM, 0, 0, spus_out1_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT2", SND_SOC_NOPM, 0, 0, spus_out2_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT3", SND_SOC_NOPM, 0, 0, spus_out3_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT4", SND_SOC_NOPM, 0, 0, spus_out4_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT5", SND_SOC_NOPM, 0, 0, spus_out5_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT6", SND_SOC_NOPM, 0, 0, spus_out6_controls),
|
|
SND_SOC_DAPM_DEMUX("SPUS OUT7", SND_SOC_NOPM, 0, 0, spus_out7_controls),
|
|
|
|
SND_SOC_DAPM_PGA("SPUS OUT0-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT1-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT2-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT3-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT4-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT5-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT6-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT7-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT0-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT1-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT2-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT3-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT4-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT5-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT6-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT7-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT0-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT1-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT2-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT3-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT4-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT5-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT6-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SPUS OUT7-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_MIXER_E("SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0,
|
|
abox_flush_mixp,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_MUX_E("SIFS1", SND_SOC_NOPM, 0, 0, sifs1_controls,
|
|
abox_flush_sifs1,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_MUX_E("SIFS2", SND_SOC_NOPM, 0, 0, sifs2_controls,
|
|
abox_flush_sifs2,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_MUX("UAIF0 SPK", SND_SOC_NOPM, 0, 0, uaif0_spk_controls),
|
|
SND_SOC_DAPM_MUX("UAIF1 SPK", SND_SOC_NOPM, 0, 0, uaif1_spk_controls),
|
|
SND_SOC_DAPM_MUX("UAIF2 SPK", SND_SOC_NOPM, 0, 0, uaif2_spk_controls),
|
|
SND_SOC_DAPM_MUX("UAIF3 SPK", SND_SOC_NOPM, 0, 0, uaif3_spk_controls),
|
|
SND_SOC_DAPM_MUX("UAIF4 SPK", SND_SOC_NOPM, 0, 0, uaif4_spk_controls),
|
|
SND_SOC_DAPM_MUX("DSIF SPK", SND_SOC_NOPM, 0, 0, dsif_spk_controls),
|
|
|
|
SND_SOC_DAPM_MUX("RSRC0", SND_SOC_NOPM, 0, 0, rsrc0_controls),
|
|
SND_SOC_DAPM_MUX("RSRC1", SND_SOC_NOPM, 0, 0, rsrc1_controls),
|
|
SND_SOC_DAPM_MUX("NSRC0", SND_SOC_NOPM, 0, 0, nsrc0_controls),
|
|
SND_SOC_DAPM_MUX("NSRC1", SND_SOC_NOPM, 0, 0, nsrc1_controls),
|
|
SND_SOC_DAPM_MUX("NSRC2", SND_SOC_NOPM, 0, 0, nsrc2_controls),
|
|
SND_SOC_DAPM_MUX("NSRC3", SND_SOC_NOPM, 0, 0, nsrc3_controls),
|
|
|
|
SND_SOC_DAPM_PGA("PIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("PIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SOC_MIXER_E_ARRAY("RECP", SND_SOC_NOPM, 0, 0, recp_controls,
|
|
abox_flush_recp,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_MUX("SPUM ASRC0", SND_SOC_NOPM, 0, 0, spum_asrc0_controls),
|
|
SND_SOC_DAPM_MUX("SPUM ASRC1", SND_SOC_NOPM, 0, 0, spum_asrc1_controls),
|
|
SND_SOC_DAPM_MUX("SPUM ASRC2", SND_SOC_NOPM, 0, 0, spum_asrc2_controls),
|
|
SND_SOC_DAPM_MUX("SPUM ASRC3", SND_SOC_NOPM, 0, 0, spum_asrc3_controls),
|
|
SND_SOC_DAPM_DEMUX_E("SIFM0", SND_SOC_NOPM, 0, 0, sifm0_controls,
|
|
abox_flush_sifm0,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DEMUX_E("SIFM1", SND_SOC_NOPM, 0, 0, sifm1_controls,
|
|
abox_flush_sifm1,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DEMUX_E("SIFM2", SND_SOC_NOPM, 0, 0, sifm2_controls,
|
|
abox_flush_sifm2,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DEMUX_E("SIFM3", SND_SOC_NOPM, 0, 0, sifm3_controls,
|
|
abox_flush_sifm3,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_PGA("SIFM0-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFM1-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFM2-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_PGA("SIFM3-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
SND_SOC_DAPM_MUX("SIFMS", SND_SOC_NOPM, 0, 0, sifms_controls),
|
|
|
|
SND_SOC_DAPM_AIF_IN("UAIF0IN", "UAIF0 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_AIF_IN("UAIF1IN", "UAIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_AIF_IN("UAIF2IN", "UAIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_AIF_IN("UAIF3IN", "UAIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_AIF_IN("UAIF4IN", "UAIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
|
|
SND_SOC_DAPM_AIF_OUT("UAIF0OUT", "UAIF0 Playback", 0, SND_SOC_NOPM,
|
|
0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("UAIF1OUT", "UAIF1 Playback", 0, SND_SOC_NOPM,
|
|
0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("UAIF2OUT", "UAIF2 Playback", 0, SND_SOC_NOPM,
|
|
0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("UAIF3OUT", "UAIF3 Playback", 0, SND_SOC_NOPM,
|
|
0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("UAIF4OUT", "UAIF4 Playback", 0, SND_SOC_NOPM,
|
|
0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("DSIFOUT", "DSIF Playback", 0, SND_SOC_NOPM, 0, 0),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route abox_cmpnt_dapm_routes[] = {
|
|
/* sink, control, source */
|
|
{"SIFSM", NULL, "SPUSM"},
|
|
{"SIFSM-SPUS IN0", "SPUS IN0", "SIFSM"},
|
|
{"SIFSM-SPUS IN1", "SPUS IN1", "SIFSM"},
|
|
{"SIFSM-SPUS IN2", "SPUS IN2", "SIFSM"},
|
|
{"SIFSM-SPUS IN3", "SPUS IN3", "SIFSM"},
|
|
{"SIFSM-SPUS IN4", "SPUS IN4", "SIFSM"},
|
|
{"SIFSM-SPUS IN5", "SPUS IN5", "SIFSM"},
|
|
{"SIFSM-SPUS IN6", "SPUS IN6", "SIFSM"},
|
|
{"SIFSM-SPUS IN7", "SPUS IN7", "SIFSM"},
|
|
|
|
{"SPUS IN0", "RDMA", "RDMA0 Playback"},
|
|
{"SPUS IN0", "SIFSM", "SIFSM-SPUS IN0"},
|
|
{"SPUS IN1", "RDMA", "RDMA1 Playback"},
|
|
{"SPUS IN1", "SIFSM", "SIFSM-SPUS IN1"},
|
|
{"SPUS IN2", "RDMA", "RDMA2 Playback"},
|
|
{"SPUS IN2", "SIFSM", "SIFSM-SPUS IN2"},
|
|
{"SPUS IN3", "RDMA", "RDMA3 Playback"},
|
|
{"SPUS IN3", "SIFSM", "SIFSM-SPUS IN3"},
|
|
{"SPUS IN4", "RDMA", "RDMA4 Playback"},
|
|
{"SPUS IN4", "SIFSM", "SIFSM-SPUS IN4"},
|
|
{"SPUS IN5", "RDMA", "RDMA5 Playback"},
|
|
{"SPUS IN5", "SIFSM", "SIFSM-SPUS IN5"},
|
|
{"SPUS IN6", "RDMA", "RDMA6 Playback"},
|
|
{"SPUS IN6", "SIFSM", "SIFSM-SPUS IN6"},
|
|
{"SPUS IN7", "RDMA", "RDMA7 Playback"},
|
|
{"SPUS IN7", "SIFSM", "SIFSM-SPUS IN7"},
|
|
|
|
{"SPUS ASRC0", "On", "SPUS IN0"},
|
|
{"SPUS ASRC0", "Off", "SPUS IN0"},
|
|
{"SPUS ASRC1", "On", "SPUS IN1"},
|
|
{"SPUS ASRC1", "Off", "SPUS IN1"},
|
|
{"SPUS ASRC2", "On", "SPUS IN2"},
|
|
{"SPUS ASRC2", "Off", "SPUS IN2"},
|
|
{"SPUS ASRC3", "On", "SPUS IN3"},
|
|
{"SPUS ASRC3", "Off", "SPUS IN3"},
|
|
{"SPUS ASRC4", "On", "SPUS IN4"},
|
|
{"SPUS ASRC4", "Off", "SPUS IN4"},
|
|
{"SPUS ASRC5", "On", "SPUS IN5"},
|
|
{"SPUS ASRC5", "Off", "SPUS IN5"},
|
|
{"SPUS ASRC6", "On", "SPUS IN6"},
|
|
{"SPUS ASRC6", "Off", "SPUS IN6"},
|
|
{"SPUS ASRC7", "On", "SPUS IN7"},
|
|
{"SPUS ASRC7", "Off", "SPUS IN7"},
|
|
|
|
{"SPUS OUT0", NULL, "SPUS ASRC0"},
|
|
{"SPUS OUT1", NULL, "SPUS ASRC1"},
|
|
{"SPUS OUT2", NULL, "SPUS ASRC2"},
|
|
{"SPUS OUT3", NULL, "SPUS ASRC3"},
|
|
{"SPUS OUT4", NULL, "SPUS ASRC4"},
|
|
{"SPUS OUT5", NULL, "SPUS ASRC5"},
|
|
{"SPUS OUT6", NULL, "SPUS ASRC6"},
|
|
{"SPUS OUT7", NULL, "SPUS ASRC7"},
|
|
|
|
{"SPUS OUT0-SIFS0", "SIFS0", "SPUS OUT0"},
|
|
{"SPUS OUT1-SIFS0", "SIFS0", "SPUS OUT1"},
|
|
{"SPUS OUT2-SIFS0", "SIFS0", "SPUS OUT2"},
|
|
{"SPUS OUT3-SIFS0", "SIFS0", "SPUS OUT3"},
|
|
{"SPUS OUT4-SIFS0", "SIFS0", "SPUS OUT4"},
|
|
{"SPUS OUT5-SIFS0", "SIFS0", "SPUS OUT5"},
|
|
{"SPUS OUT6-SIFS0", "SIFS0", "SPUS OUT6"},
|
|
{"SPUS OUT7-SIFS0", "SIFS0", "SPUS OUT7"},
|
|
{"SPUS OUT0-SIFS1", "SIFS1", "SPUS OUT0"},
|
|
{"SPUS OUT1-SIFS1", "SIFS1", "SPUS OUT1"},
|
|
{"SPUS OUT2-SIFS1", "SIFS1", "SPUS OUT2"},
|
|
{"SPUS OUT3-SIFS1", "SIFS1", "SPUS OUT3"},
|
|
{"SPUS OUT4-SIFS1", "SIFS1", "SPUS OUT4"},
|
|
{"SPUS OUT5-SIFS1", "SIFS1", "SPUS OUT5"},
|
|
{"SPUS OUT6-SIFS1", "SIFS1", "SPUS OUT6"},
|
|
{"SPUS OUT7-SIFS1", "SIFS1", "SPUS OUT7"},
|
|
{"SPUS OUT0-SIFS2", "SIFS2", "SPUS OUT0"},
|
|
{"SPUS OUT1-SIFS2", "SIFS2", "SPUS OUT1"},
|
|
{"SPUS OUT2-SIFS2", "SIFS2", "SPUS OUT2"},
|
|
{"SPUS OUT3-SIFS2", "SIFS2", "SPUS OUT3"},
|
|
{"SPUS OUT4-SIFS2", "SIFS2", "SPUS OUT4"},
|
|
{"SPUS OUT5-SIFS2", "SIFS2", "SPUS OUT5"},
|
|
{"SPUS OUT6-SIFS2", "SIFS2", "SPUS OUT6"},
|
|
{"SPUS OUT7-SIFS2", "SIFS2", "SPUS OUT7"},
|
|
|
|
{"SIFS0", NULL, "SPUS OUT0-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT1-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT2-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT3-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT4-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT5-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT6-SIFS0"},
|
|
{"SIFS0", NULL, "SPUS OUT7-SIFS0"},
|
|
{"SIFS1", "SPUS OUT0", "SPUS OUT0-SIFS1"},
|
|
{"SIFS1", "SPUS OUT1", "SPUS OUT1-SIFS1"},
|
|
{"SIFS1", "SPUS OUT2", "SPUS OUT2-SIFS1"},
|
|
{"SIFS1", "SPUS OUT3", "SPUS OUT3-SIFS1"},
|
|
{"SIFS1", "SPUS OUT4", "SPUS OUT4-SIFS1"},
|
|
{"SIFS1", "SPUS OUT5", "SPUS OUT5-SIFS1"},
|
|
{"SIFS1", "SPUS OUT6", "SPUS OUT6-SIFS1"},
|
|
{"SIFS1", "SPUS OUT7", "SPUS OUT7-SIFS1"},
|
|
{"SIFS2", "SPUS OUT0", "SPUS OUT0-SIFS2"},
|
|
{"SIFS2", "SPUS OUT1", "SPUS OUT1-SIFS2"},
|
|
{"SIFS2", "SPUS OUT2", "SPUS OUT2-SIFS2"},
|
|
{"SIFS2", "SPUS OUT3", "SPUS OUT3-SIFS2"},
|
|
{"SIFS2", "SPUS OUT4", "SPUS OUT4-SIFS2"},
|
|
{"SIFS2", "SPUS OUT5", "SPUS OUT5-SIFS2"},
|
|
{"SIFS2", "SPUS OUT6", "SPUS OUT6-SIFS2"},
|
|
{"SIFS2", "SPUS OUT7", "SPUS OUT7-SIFS2"},
|
|
|
|
{"SIFS0 Playback", NULL, "SIFS0"},
|
|
{"SIFS1 Playback", NULL, "SIFS1"},
|
|
{"SIFS2 Playback", NULL, "SIFS2"},
|
|
|
|
{"UAIF0 SPK", "SIFS0", "SIFS0"},
|
|
{"UAIF0 SPK", "SIFS1", "SIFS1"},
|
|
{"UAIF0 SPK", "SIFS2", "SIFS2"},
|
|
{"UAIF0 SPK", "SIFMS", "SIFMS"},
|
|
{"UAIF1 SPK", "SIFS0", "SIFS0"},
|
|
{"UAIF1 SPK", "SIFS1", "SIFS1"},
|
|
{"UAIF1 SPK", "SIFS2", "SIFS2"},
|
|
{"UAIF1 SPK", "SIFMS", "SIFMS"},
|
|
{"UAIF2 SPK", "SIFS0", "SIFS0"},
|
|
{"UAIF2 SPK", "SIFS1", "SIFS1"},
|
|
{"UAIF2 SPK", "SIFS2", "SIFS2"},
|
|
{"UAIF2 SPK", "SIFMS", "SIFMS"},
|
|
{"UAIF3 SPK", "SIFS0", "SIFS0"},
|
|
{"UAIF3 SPK", "SIFS1", "SIFS1"},
|
|
{"UAIF3 SPK", "SIFS2", "SIFS2"},
|
|
{"UAIF3 SPK", "SIFMS", "SIFMS"},
|
|
{"UAIF4 SPK", "SIFS0", "SIFS0"},
|
|
{"UAIF4 SPK", "SIFS1", "SIFS1"},
|
|
{"UAIF4 SPK", "SIFS2", "SIFS2"},
|
|
{"UAIF4 SPK", "SIFMS", "SIFMS"},
|
|
{"DSIF SPK", "SIFS1", "SIFS1"},
|
|
{"DSIF SPK", "SIFS2", "SIFS2"},
|
|
|
|
{"RSRC0", "SIFS0", "SIFS0 Capture"},
|
|
{"RSRC0", "SIFS1", "SIFS1 Capture"},
|
|
{"RSRC0", "SIFS2", "SIFS2 Capture"},
|
|
{"RSRC0", "NSRC0", "NSRC0"},
|
|
{"RSRC0", "NSRC1", "NSRC1"},
|
|
{"RSRC0", "NSRC2", "NSRC2"},
|
|
{"RSRC0", "NSRC3", "NSRC3"},
|
|
{"RSRC1", "SIFS0", "SIFS0 Capture"},
|
|
{"RSRC1", "SIFS1", "SIFS1 Capture"},
|
|
{"RSRC1", "SIFS2", "SIFS2 Capture"},
|
|
{"RSRC1", "NSRC0", "NSRC0"},
|
|
{"RSRC1", "NSRC1", "NSRC1"},
|
|
{"RSRC1", "NSRC2", "NSRC2"},
|
|
{"RSRC1", "NSRC3", "NSRC3"},
|
|
|
|
{"NSRC0", "SIFS0", "SIFS0 Capture"},
|
|
{"NSRC0", "SIFS1", "SIFS1 Capture"},
|
|
{"NSRC0", "SIFS2", "SIFS2 Capture"},
|
|
{"NSRC1", "SIFS0", "SIFS0 Capture"},
|
|
{"NSRC1", "SIFS1", "SIFS1 Capture"},
|
|
{"NSRC1", "SIFS2", "SIFS2 Capture"},
|
|
{"NSRC2", "SIFS0", "SIFS0 Capture"},
|
|
{"NSRC2", "SIFS1", "SIFS1 Capture"},
|
|
{"NSRC2", "SIFS2", "SIFS2 Capture"},
|
|
{"NSRC3", "SIFS0", "SIFS0 Capture"},
|
|
{"NSRC3", "SIFS1", "SIFS1 Capture"},
|
|
{"NSRC3", "SIFS2", "SIFS2 Capture"},
|
|
|
|
{"PIFS0", NULL, "RSRC0"},
|
|
{"PIFS1", NULL, "RSRC1"},
|
|
{"RECP", "PIFS0", "PIFS0"},
|
|
{"RECP", "PIFS1", "PIFS1"},
|
|
|
|
{"SPUM ASRC0", "On", "RECP"},
|
|
{"SPUM ASRC0", "Off", "RECP"},
|
|
{"SPUM ASRC1", "On", "NSRC0"},
|
|
{"SPUM ASRC1", "Off", "NSRC0"},
|
|
{"SPUM ASRC2", "On", "NSRC1"},
|
|
{"SPUM ASRC2", "Off", "NSRC1"},
|
|
{"SPUM ASRC3", "On", "NSRC2"},
|
|
{"SPUM ASRC3", "Off", "NSRC2"},
|
|
|
|
{"SIFM0", NULL, "SPUM ASRC1"},
|
|
{"SIFM1", NULL, "SPUM ASRC2"},
|
|
{"SIFM2", NULL, "SPUM ASRC3"},
|
|
{"SIFM3", NULL, "NSRC3"},
|
|
|
|
{"SIFM0-SIFMS", "SIFMS", "SIFM0"},
|
|
{"SIFM1-SIFMS", "SIFMS", "SIFM1"},
|
|
{"SIFM2-SIFMS", "SIFMS", "SIFM2"},
|
|
{"SIFM3-SIFMS", "SIFMS", "SIFM3"},
|
|
|
|
{"SIFMS", "SIFM0", "SIFM0-SIFMS"},
|
|
{"SIFMS", "SIFM1", "SIFM1-SIFMS"},
|
|
{"SIFMS", "SIFM2", "SIFM2-SIFMS"},
|
|
{"SIFMS", "SIFM3", "SIFM3-SIFMS"},
|
|
|
|
{"WDMA0 Capture", NULL, "SPUM ASRC0"},
|
|
{"WDMA1 Capture", "WDMA", "SIFM0"},
|
|
{"WDMA2 Capture", "WDMA", "SIFM1"},
|
|
{"WDMA3 Capture", "WDMA", "SIFM2"},
|
|
{"WDMA4 Capture", "WDMA", "SIFM3"},
|
|
};
|
|
|
|
static bool abox_volatile_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case ABOX_SYSPOWER_CTRL:
|
|
case ABOX_SYSPOWER_STATUS:
|
|
case ABOX_SPUS_CTRL2:
|
|
case ABOX_SPUS_CTRL3:
|
|
case ABOX_SPUM_CTRL2:
|
|
case ABOX_SPUM_CTRL3:
|
|
case ABOX_UAIF_STATUS(0):
|
|
case ABOX_UAIF_STATUS(1):
|
|
case ABOX_UAIF_STATUS(2):
|
|
case ABOX_UAIF_STATUS(3):
|
|
case ABOX_UAIF_STATUS(4):
|
|
case ABOX_DSIF_STATUS:
|
|
case ABOX_TIMER_CTRL0(0):
|
|
case ABOX_TIMER_CTRL1(0):
|
|
case ABOX_TIMER_CTRL0(1):
|
|
case ABOX_TIMER_CTRL1(1):
|
|
case ABOX_TIMER_CTRL0(2):
|
|
case ABOX_TIMER_CTRL1(2):
|
|
case ABOX_TIMER_CTRL0(3):
|
|
case ABOX_TIMER_CTRL1(3):
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool abox_readable_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case ABOX_IP_INDEX:
|
|
case ABOX_VERSION:
|
|
case ABOX_SYSPOWER_CTRL:
|
|
case ABOX_SYSPOWER_STATUS:
|
|
case ABOX_SYSTEM_CONFIG0:
|
|
case ABOX_REMAP_MASK:
|
|
case ABOX_REMAP_ADDR:
|
|
case ABOX_DYN_CLOCK_OFF:
|
|
case ABOX_QCHANNEL_DISABLE:
|
|
case ABOX_ROUTE_CTRL0:
|
|
case ABOX_ROUTE_CTRL1:
|
|
case ABOX_ROUTE_CTRL2:
|
|
case ABOX_TICK_DIV_RATIO:
|
|
case ABOX_SPUS_CTRL0:
|
|
case ABOX_SPUS_CTRL1:
|
|
case ABOX_SPUS_CTRL2:
|
|
case ABOX_SPUS_CTRL3:
|
|
case ABOX_SPUS_CTRL_SIFS_CNT0:
|
|
case ABOX_SPUS_CTRL_SIFS_CNT1:
|
|
case ABOX_SPUM_CTRL0:
|
|
case ABOX_SPUM_CTRL1:
|
|
case ABOX_SPUM_CTRL2:
|
|
case ABOX_SPUM_CTRL3:
|
|
case ABOX_UAIF_CTRL0(0):
|
|
case ABOX_UAIF_CTRL1(0):
|
|
case ABOX_UAIF_STATUS(0):
|
|
case ABOX_UAIF_CTRL0(1):
|
|
case ABOX_UAIF_CTRL1(1):
|
|
case ABOX_UAIF_STATUS(1):
|
|
case ABOX_UAIF_CTRL0(2):
|
|
case ABOX_UAIF_CTRL1(2):
|
|
case ABOX_UAIF_STATUS(2):
|
|
case ABOX_UAIF_CTRL0(3):
|
|
case ABOX_UAIF_CTRL1(3):
|
|
case ABOX_UAIF_STATUS(3):
|
|
case ABOX_UAIF_CTRL0(4):
|
|
case ABOX_UAIF_CTRL1(4):
|
|
case ABOX_UAIF_STATUS(4):
|
|
case ABOX_DSIF_CTRL:
|
|
case ABOX_DSIF_STATUS:
|
|
case ABOX_SPDYIF_CTRL:
|
|
case ABOX_TIMER_CTRL0(0):
|
|
case ABOX_TIMER_CTRL1(0):
|
|
case ABOX_TIMER_CTRL0(1):
|
|
case ABOX_TIMER_CTRL1(1):
|
|
case ABOX_TIMER_CTRL0(2):
|
|
case ABOX_TIMER_CTRL1(2):
|
|
case ABOX_TIMER_CTRL0(3):
|
|
case ABOX_TIMER_CTRL1(3):
|
|
case ABOX_RDMA_VOL_FACTOR(0):
|
|
case ABOX_RDMA_VOL_FACTOR(1):
|
|
case ABOX_RDMA_VOL_FACTOR(2):
|
|
case ABOX_RDMA_VOL_FACTOR(3):
|
|
case ABOX_RDMA_VOL_FACTOR(4):
|
|
case ABOX_RDMA_VOL_FACTOR(5):
|
|
case ABOX_RDMA_VOL_FACTOR(6):
|
|
case ABOX_RDMA_VOL_FACTOR(7):
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool abox_writeable_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case ABOX_SYSPOWER_CTRL:
|
|
case ABOX_SYSTEM_CONFIG0:
|
|
case ABOX_REMAP_MASK:
|
|
case ABOX_REMAP_ADDR:
|
|
case ABOX_DYN_CLOCK_OFF:
|
|
case ABOX_QCHANNEL_DISABLE:
|
|
case ABOX_ROUTE_CTRL0:
|
|
case ABOX_ROUTE_CTRL1:
|
|
case ABOX_ROUTE_CTRL2:
|
|
case ABOX_SPUS_CTRL0:
|
|
case ABOX_SPUS_CTRL1:
|
|
case ABOX_SPUS_CTRL2:
|
|
case ABOX_SPUS_CTRL3:
|
|
case ABOX_SPUS_CTRL_SIFS_CNT0:
|
|
case ABOX_SPUS_CTRL_SIFS_CNT1:
|
|
case ABOX_SPUM_CTRL0:
|
|
case ABOX_SPUM_CTRL1:
|
|
case ABOX_SPUM_CTRL2:
|
|
case ABOX_SPUM_CTRL3:
|
|
case ABOX_UAIF_CTRL0(0):
|
|
case ABOX_UAIF_CTRL1(0):
|
|
case ABOX_UAIF_CTRL0(1):
|
|
case ABOX_UAIF_CTRL1(1):
|
|
case ABOX_UAIF_CTRL0(2):
|
|
case ABOX_UAIF_CTRL1(2):
|
|
case ABOX_UAIF_CTRL0(3):
|
|
case ABOX_UAIF_CTRL1(3):
|
|
case ABOX_UAIF_CTRL0(4):
|
|
case ABOX_UAIF_CTRL1(4):
|
|
case ABOX_DSIF_CTRL:
|
|
case ABOX_SPDYIF_CTRL:
|
|
case ABOX_TIMER_CTRL0(0):
|
|
case ABOX_TIMER_CTRL1(0):
|
|
case ABOX_TIMER_CTRL0(1):
|
|
case ABOX_TIMER_CTRL1(1):
|
|
case ABOX_TIMER_CTRL0(2):
|
|
case ABOX_TIMER_CTRL1(2):
|
|
case ABOX_TIMER_CTRL0(3):
|
|
case ABOX_TIMER_CTRL1(3):
|
|
case ABOX_RDMA_VOL_FACTOR(0):
|
|
case ABOX_RDMA_VOL_FACTOR(1):
|
|
case ABOX_RDMA_VOL_FACTOR(2):
|
|
case ABOX_RDMA_VOL_FACTOR(3):
|
|
case ABOX_RDMA_VOL_FACTOR(4):
|
|
case ABOX_RDMA_VOL_FACTOR(5):
|
|
case ABOX_RDMA_VOL_FACTOR(6):
|
|
case ABOX_RDMA_VOL_FACTOR(7):
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const struct reg_default abox_reg_defaults_8895[] = {
|
|
{0x0000, 0x41424F58},
|
|
{0x0010, 0x00000000},
|
|
{0x0014, 0x00000000},
|
|
{0x0020, 0x00000000},
|
|
{0x0024, 0xFFF00000},
|
|
{0x0028, 0x13F00000},
|
|
{0x0030, 0x7FFFFFFF},
|
|
{0x0040, 0x00000000},
|
|
{0x0044, 0x00000000},
|
|
{0x0048, 0x00000000},
|
|
{0x0200, 0x00000000},
|
|
{0x0204, 0x00000000},
|
|
{0x0208, 0x00000000},
|
|
{0x020C, 0x00000000},
|
|
{0x0220, 0x00000000},
|
|
{0x0224, 0x00000000},
|
|
{0x0228, 0x00000000},
|
|
{0x022C, 0x00000000},
|
|
{0x0230, 0x00000000},
|
|
{0x0234, 0x00000000},
|
|
{0x0238, 0x00000000},
|
|
{0x023C, 0x00000000},
|
|
{0x0240, 0x00000000},
|
|
{0x0260, 0x00000000},
|
|
{0x0300, 0x00000000},
|
|
{0x0304, 0x00000000},
|
|
{0x0308, 0x00000000},
|
|
{0x030C, 0x00000000},
|
|
{0x0320, 0x00000000},
|
|
{0x0324, 0x00000000},
|
|
{0x0328, 0x00000000},
|
|
{0x032C, 0x00000000},
|
|
{0x0330, 0x00000000},
|
|
{0x0334, 0x00000000},
|
|
{0x0338, 0x00000000},
|
|
{0x033C, 0x00000000},
|
|
{0x0340, 0x00000000},
|
|
{0x0344, 0x00000000},
|
|
{0x0348, 0x00000000},
|
|
{0x0500, 0x01000000},
|
|
{0x0504, 0x00000000},
|
|
{0x050C, 0x00000000},
|
|
{0x0510, 0x01000000},
|
|
{0x0514, 0x00000000},
|
|
{0x051C, 0x00000000},
|
|
{0x0520, 0x01000000},
|
|
{0x0524, 0x00000000},
|
|
{0x052C, 0x00000000},
|
|
{0x0530, 0x01000000},
|
|
{0x0534, 0x00000000},
|
|
{0x053C, 0x00000000},
|
|
{0x0540, 0x01000000},
|
|
{0x0544, 0x00000000},
|
|
{0x054C, 0x00000000},
|
|
{0x0550, 0x00000000},
|
|
{0x0554, 0x00000000},
|
|
};
|
|
|
|
static const struct reg_default abox_reg_defaults_9810[] = {
|
|
{0x0000, 0x41424F58},
|
|
{0x0010, 0x00000000},
|
|
{0x0014, 0x00000000},
|
|
{0x0020, 0x00004444},
|
|
{0x0024, 0xFFF00000},
|
|
{0x0028, 0x17D00000},
|
|
{0x0030, 0x7FFFFFFF},
|
|
{0x0038, 0x00000000},
|
|
{0x0040, 0x00000000},
|
|
{0x0044, 0x00000000},
|
|
{0x0048, 0x00000000},
|
|
{0x0200, 0x00000000},
|
|
{0x0204, 0x00000000},
|
|
{0x0208, 0x00000000},
|
|
{0x020C, 0x00000000},
|
|
{0x0220, 0x00000000},
|
|
{0x0224, 0x00000000},
|
|
{0x0228, 0x00000000},
|
|
{0x022C, 0x00000000},
|
|
{0x0230, 0x00000000},
|
|
{0x0234, 0x00000000},
|
|
{0x0238, 0x00000000},
|
|
{0x023C, 0x00000000},
|
|
{0x0240, 0x00000000},
|
|
{0x0260, 0x00000000},
|
|
{0x0280, 0x00000000},
|
|
{0x0284, 0x00000000},
|
|
{0x0300, 0x00000000},
|
|
{0x0304, 0x00000000},
|
|
{0x0308, 0x00000000},
|
|
{0x030C, 0x00000000},
|
|
{0x0320, 0x00000000},
|
|
{0x0324, 0x00000000},
|
|
{0x0328, 0x00000000},
|
|
{0x032C, 0x00000000},
|
|
{0x0330, 0x00000000},
|
|
{0x0334, 0x00000000},
|
|
{0x0338, 0x00000000},
|
|
{0x033C, 0x00000000},
|
|
{0x0340, 0x00000000},
|
|
{0x0344, 0x00000000},
|
|
{0x0348, 0x00000000},
|
|
{0x0500, 0x01000010},
|
|
{0x0504, 0x00000000},
|
|
{0x050C, 0x00000000},
|
|
{0x0510, 0x01000010},
|
|
{0x0514, 0x00000000},
|
|
{0x051C, 0x00000000},
|
|
{0x0520, 0x01000010},
|
|
{0x0524, 0x00000000},
|
|
{0x052C, 0x00000000},
|
|
{0x0530, 0x01000010},
|
|
{0x0534, 0x00000000},
|
|
{0x053C, 0x00000000},
|
|
{0x0540, 0x01000010},
|
|
{0x0544, 0x00000000},
|
|
{0x054C, 0x00000000},
|
|
{0x0550, 0x00000000},
|
|
{0x0554, 0x00000000},
|
|
{0x1318, 0x00000000},
|
|
};
|
|
|
|
static const struct reg_default abox_reg_defaults_9610[] = {
|
|
{0x0000, 0x41424F58},
|
|
{0x0010, 0x00000000},
|
|
{0x0014, 0x00000000},
|
|
{0x0020, 0x00000000},
|
|
{0x0024, 0xFFF00000},
|
|
{0x0028, 0x14B00000},
|
|
{0x0030, 0x7FFFFFFF},
|
|
{0x0038, 0x00000000},
|
|
{0x0040, 0x00000000},
|
|
{0x0044, 0x00000000},
|
|
{0x0048, 0x00000000},
|
|
{0x0050, 0x000004E1},
|
|
{0x0200, 0x00000000},
|
|
{0x0204, 0x00000000},
|
|
{0x0208, 0x00000000},
|
|
{0x020C, 0x00000000},
|
|
{0x0220, 0x00000000},
|
|
{0x0224, 0x00000000},
|
|
{0x0228, 0x00000000},
|
|
{0x022C, 0x00000000},
|
|
{0x0230, 0x00000000},
|
|
{0x0234, 0x00000000},
|
|
{0x0238, 0x00000000},
|
|
{0x023C, 0x00000000},
|
|
{0x0240, 0x00000000},
|
|
{0x0244, 0x00000000},
|
|
{0x0248, 0x00000000},
|
|
{0x024C, 0x00000000},
|
|
{0x0250, 0x00000000},
|
|
{0x0254, 0x00000000},
|
|
{0x0258, 0x00000000},
|
|
{0x025C, 0x00000000},
|
|
{0x0260, 0x00000000},
|
|
{0x0280, 0x00000000},
|
|
{0x0284, 0x00000000},
|
|
{0x0300, 0x00000000},
|
|
{0x0304, 0x00000000},
|
|
{0x0308, 0x00000000},
|
|
{0x030C, 0x00000000},
|
|
{0x0320, 0x00000000},
|
|
{0x0324, 0x00000000},
|
|
{0x0328, 0x00000000},
|
|
{0x032C, 0x00000000},
|
|
{0x0330, 0x00000000},
|
|
{0x0334, 0x00000000},
|
|
{0x0338, 0x00000000},
|
|
{0x033C, 0x00000000},
|
|
{0x0340, 0x00000000},
|
|
{0x0344, 0x00000000},
|
|
{0x0348, 0x00000000},
|
|
{0x0500, 0x01000010},
|
|
{0x0504, 0x00000000},
|
|
{0x050C, 0x00000000},
|
|
{0x0510, 0x01000010},
|
|
{0x0514, 0x00000000},
|
|
{0x051C, 0x00000000},
|
|
{0x0520, 0x01000010},
|
|
{0x0524, 0x00000000},
|
|
{0x052C, 0x00000000},
|
|
{0x0530, 0x01000010},
|
|
{0x0534, 0x00000000},
|
|
{0x053C, 0x00000000},
|
|
{0x0540, 0x01000010},
|
|
{0x0544, 0x00000000},
|
|
{0x054C, 0x00000000},
|
|
{0x0550, 0x00000000},
|
|
{0x0554, 0x00000000},
|
|
{0x0560, 0x00000000},
|
|
{0x1318, 0x00000000},
|
|
};
|
|
|
|
static struct regmap_config abox_regmap_config = {
|
|
.reg_bits = 32,
|
|
.val_bits = 32,
|
|
.reg_stride = 4,
|
|
.max_register = ABOX_MAX_REGISTERS,
|
|
.volatile_reg = abox_volatile_reg,
|
|
.readable_reg = abox_readable_reg,
|
|
.writeable_reg = abox_writeable_reg,
|
|
.cache_type = REGCACHE_RBTREE,
|
|
.fast_io = true,
|
|
};
|
|
|
|
static const struct snd_soc_component_driver abox_cmpnt = {
|
|
.probe = abox_cmpnt_probe,
|
|
.remove = abox_cmpnt_remove,
|
|
.controls = abox_cmpnt_controls,
|
|
.num_controls = ARRAY_SIZE(abox_cmpnt_controls),
|
|
.dapm_widgets = abox_cmpnt_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(abox_cmpnt_dapm_widgets),
|
|
.dapm_routes = abox_cmpnt_dapm_routes,
|
|
.num_dapm_routes = ARRAY_SIZE(abox_cmpnt_dapm_routes),
|
|
.probe_order = SND_SOC_COMP_ORDER_FIRST,
|
|
};
|
|
|
|
struct abox_name_rate_format {
|
|
const char *name;
|
|
int stream;
|
|
const enum ABOX_CONFIGMSG rate;
|
|
const enum ABOX_CONFIGMSG format;
|
|
bool slave;
|
|
};
|
|
|
|
static const struct abox_name_rate_format abox_nrf[] = {
|
|
{"SIFS0", SNDRV_PCM_STREAM_PLAYBACK, SET_MIXER_SAMPLE_RATE,
|
|
SET_MIXER_FORMAT, false},
|
|
{"SIFS1", SNDRV_PCM_STREAM_PLAYBACK, SET_OUT1_SAMPLE_RATE,
|
|
SET_OUT1_FORMAT, false},
|
|
{"SIFS2", SNDRV_PCM_STREAM_PLAYBACK, SET_OUT2_SAMPLE_RATE,
|
|
SET_OUT2_FORMAT, false},
|
|
{"RECP", SNDRV_PCM_STREAM_CAPTURE, SET_RECP_SAMPLE_RATE,
|
|
SET_RECP_FORMAT, true},
|
|
{"SIFM0", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX0_SAMPLE_RATE,
|
|
SET_INMUX0_FORMAT, false},
|
|
{"SIFM1", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX1_SAMPLE_RATE,
|
|
SET_INMUX1_FORMAT, false},
|
|
{"SIFM2", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX2_SAMPLE_RATE,
|
|
SET_INMUX2_FORMAT, false},
|
|
{"SIFM3", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX3_SAMPLE_RATE,
|
|
SET_INMUX3_FORMAT, false},
|
|
};
|
|
|
|
static bool abox_find_nrf_stream(const struct snd_soc_dapm_widget *w,
|
|
int stream, enum ABOX_CONFIGMSG *rate,
|
|
enum ABOX_CONFIGMSG *format, bool *slave)
|
|
{
|
|
struct snd_soc_component *cmpnt = w->dapm->component;
|
|
const char *name_prefix = cmpnt ? cmpnt->name_prefix : NULL;
|
|
size_t prefix_len = name_prefix ? strlen(name_prefix) + 1 : 0;
|
|
const char *name = w->name + prefix_len;
|
|
const struct abox_name_rate_format *nrf;
|
|
|
|
for (nrf = abox_nrf; nrf - abox_nrf < ARRAY_SIZE(abox_nrf); nrf++) {
|
|
if ((nrf->stream == stream) && (strcmp(nrf->name, name) == 0)) {
|
|
*rate = nrf->rate;
|
|
*format = nrf->format;
|
|
*slave = nrf->slave;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool abox_find_nrf(const struct snd_soc_dapm_widget *w,
|
|
enum ABOX_CONFIGMSG *rate, enum ABOX_CONFIGMSG *format,
|
|
int *stream, bool *slave)
|
|
{
|
|
struct snd_soc_component *cmpnt = w->dapm->component;
|
|
const char *name_prefix = cmpnt ? cmpnt->name_prefix : NULL;
|
|
size_t prefix_len = name_prefix ? strlen(name_prefix) + 1 : 0;
|
|
const char *name = w->name + prefix_len;
|
|
const struct abox_name_rate_format *nrf;
|
|
|
|
for (nrf = abox_nrf; nrf - abox_nrf < ARRAY_SIZE(abox_nrf); nrf++) {
|
|
if (strcmp(nrf->name, name) == 0) {
|
|
*rate = nrf->rate;
|
|
*format = nrf->format;
|
|
*stream = nrf->stream;
|
|
*slave = nrf->slave;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static struct snd_soc_dapm_widget *abox_sync_params(struct abox_data *data,
|
|
struct list_head *widget_list, int *stream,
|
|
const struct snd_soc_dapm_widget *w_src,
|
|
enum ABOX_CONFIGMSG rate, enum ABOX_CONFIGMSG format)
|
|
{
|
|
struct device *dev = &data->pdev->dev;
|
|
enum ABOX_CONFIGMSG msg_rate, msg_format;
|
|
struct snd_soc_dapm_widget *w = NULL;
|
|
bool slave;
|
|
|
|
|
|
list_for_each_entry(w, widget_list, work_list) {
|
|
if (!abox_find_nrf(w, &msg_rate, &msg_format, stream, &slave))
|
|
continue;
|
|
if (slave)
|
|
continue;
|
|
|
|
dev_dbg(dev, "%s: %s => %s\n", __func__, w->name, w_src->name);
|
|
abox_set_sif_format(data, format,
|
|
abox_get_sif_format(data, msg_format));
|
|
abox_set_sif_rate(data, rate,
|
|
abox_get_sif_rate(data, msg_rate));
|
|
abox_set_sif_channels(data, format,
|
|
abox_get_sif_channels(data, msg_format));
|
|
break;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
int abox_hw_params_fixup_helper(struct snd_soc_pcm_runtime *rtd,
|
|
struct snd_pcm_hw_params *params, int stream)
|
|
{
|
|
struct snd_soc_dai *dai = rtd->cpu_dai;
|
|
struct snd_soc_component *cmpnt = dai->component;
|
|
struct device *dev = is_abox(dai->dev) ? dai->dev : dai->dev->parent;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct snd_soc_dapm_widget *w, *w_tgt = NULL;
|
|
struct snd_soc_dapm_path *path;
|
|
LIST_HEAD(widget_list);
|
|
enum ABOX_CONFIGMSG msg_rate, msg_format;
|
|
unsigned int rate, channels, width;
|
|
snd_pcm_format_t format;
|
|
struct snd_soc_dapm_widget *w_mst = NULL;
|
|
int stream_mst;
|
|
|
|
dev_info(dev, "%s[%s](%d)\n", __func__, dai->name, stream);
|
|
|
|
if (params_channels(params) < 1) {
|
|
dev_info(dev, "channel is fixed from %d to 2\n",
|
|
params_channels(params));
|
|
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = 2;
|
|
}
|
|
|
|
if (params_width(params) < 16) {
|
|
dev_info(dev, "width is fixed from %d to 16\n",
|
|
params_width(params));
|
|
params_set_format(params, SNDRV_PCM_FORMAT_S16);
|
|
}
|
|
|
|
snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(cmpnt));
|
|
|
|
/*
|
|
* For snd_soc_dapm_connected_{output,input}_ep fully discover the graph
|
|
* we need to reset the cached number of inputs and outputs.
|
|
*/
|
|
list_for_each_entry(w, &cmpnt->card->widgets, list) {
|
|
w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
|
|
w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
|
|
}
|
|
|
|
if (!dai->playback_widget)
|
|
goto skip_playback;
|
|
snd_soc_dapm_widget_for_each_source_path(dai->playback_widget, path) {
|
|
if (path->connect) {
|
|
w = path->node[SND_SOC_DAPM_DIR_IN];
|
|
snd_soc_dapm_connected_input_ep(w, &widget_list);
|
|
}
|
|
}
|
|
skip_playback:
|
|
if (!dai->capture_widget)
|
|
goto skip_capture;
|
|
snd_soc_dapm_widget_for_each_sink_path(dai->capture_widget, path) {
|
|
if (path->connect) {
|
|
w = path->node[SND_SOC_DAPM_DIR_OUT];
|
|
snd_soc_dapm_connected_output_ep(w, &widget_list);
|
|
}
|
|
}
|
|
skip_capture:
|
|
|
|
/* find current params */
|
|
list_for_each_entry(w, &widget_list, work_list) {
|
|
bool slave;
|
|
|
|
dev_dbg(dev, "%s\n", w->name);
|
|
if (!abox_find_nrf_stream(w, stream, &msg_rate, &msg_format,
|
|
&slave))
|
|
continue;
|
|
|
|
if (slave)
|
|
w_mst = abox_sync_params(data, &widget_list, &stream_mst,
|
|
w, msg_rate, msg_format);
|
|
|
|
format = abox_get_sif_format(data, msg_format);
|
|
width = snd_pcm_format_width(format);
|
|
rate = abox_get_sif_rate(data, msg_rate);
|
|
channels = abox_get_sif_channels(data, msg_format);
|
|
dev_dbg(dev, "%s: %s: find %d bit, %u channel, %uHz\n",
|
|
__func__, w->name, width, channels, rate);
|
|
w_tgt = w;
|
|
break;
|
|
}
|
|
|
|
if (!w_tgt)
|
|
goto unlock;
|
|
|
|
if (!w_mst) {
|
|
w_mst = w_tgt;
|
|
stream_mst = stream;
|
|
}
|
|
|
|
/* channel mixing isn't supported */
|
|
abox_set_sif_channels(data, msg_format, params_channels(params));
|
|
|
|
/* override formats to UAIF format, if it is connected */
|
|
if (abox_if_hw_params_fixup(rtd, params, stream) >= 0) {
|
|
abox_set_sif_auto_config(data, msg_rate, true);
|
|
} else if (w_mst) {
|
|
list_for_each_entry(w, &cmpnt->card->widgets, list) {
|
|
w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
|
|
w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
|
|
}
|
|
list_del_init(&widget_list);
|
|
if (stream_mst == SNDRV_PCM_STREAM_PLAYBACK)
|
|
snd_soc_dapm_connected_output_ep(w_mst, &widget_list);
|
|
else
|
|
snd_soc_dapm_connected_input_ep(w_mst, &widget_list);
|
|
|
|
list_for_each_entry(w, &widget_list, work_list) {
|
|
struct snd_soc_dai *dai;
|
|
|
|
if (!w->sname)
|
|
continue;
|
|
|
|
dai = w->priv;
|
|
if (abox_if_hw_params_fixup_by_dai(dai, params, stream)
|
|
>= 0) {
|
|
abox_set_sif_auto_config(data, msg_rate, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!abox_get_sif_auto_config(data, msg_rate))
|
|
goto unlock;
|
|
|
|
format = params_format(params);
|
|
width = params_width(params);
|
|
rate = params_rate(params);
|
|
channels = params_channels(params);
|
|
|
|
if (dai->driver->symmetric_samplebits && dai->sample_width &&
|
|
dai->sample_width != width) {
|
|
width = dai->sample_width;
|
|
abox_set_sif_width(data, msg_format, dai->sample_width);
|
|
format = abox_get_sif_format(data, msg_format);
|
|
}
|
|
|
|
if (dai->driver->symmetric_channels && dai->channels &&
|
|
dai->channels != channels)
|
|
channels = dai->channels;
|
|
|
|
if (dai->driver->symmetric_rates && dai->rate && dai->rate != rate)
|
|
rate = dai->rate;
|
|
|
|
dev_dbg(dev, "%s: set to %u bit, %u channel, %uHz\n", __func__,
|
|
width, channels, rate);
|
|
unlock:
|
|
snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(cmpnt));
|
|
|
|
if (!w_tgt)
|
|
goto out;
|
|
|
|
abox_sample_rate_put_ipc(dev, rate, msg_rate);
|
|
abox_sif_format_put_ipc(dev, format, channels, msg_format);
|
|
|
|
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
|
|
abox_get_sif_rate(data, msg_rate);
|
|
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min =
|
|
abox_get_sif_channels(data, msg_format);
|
|
snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT));
|
|
snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
|
|
abox_get_sif_format(data, msg_format));
|
|
dev_info(dev, "%s: %s: %d bit, %u channel, %uHz\n",
|
|
__func__, w_tgt->name,
|
|
abox_get_sif_width(data, msg_format),
|
|
abox_get_sif_channels(data, msg_format),
|
|
abox_get_sif_rate(data, msg_rate));
|
|
out:
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(abox_hw_params_fixup_helper);
|
|
|
|
static struct pm_qos_request abox_pm_qos_aud;
|
|
static struct pm_qos_request abox_pm_qos_int;
|
|
static struct pm_qos_request abox_pm_qos_mif;
|
|
static struct pm_qos_request abox_pm_qos_lit;
|
|
static struct pm_qos_request abox_pm_qos_big;
|
|
|
|
unsigned int abox_get_requiring_int_freq_in_khz(void)
|
|
{
|
|
struct abox_data *data = p_abox_data;
|
|
unsigned int gear;
|
|
unsigned int int_freq;
|
|
|
|
if (data == NULL)
|
|
return 0;
|
|
|
|
gear = data->cpu_gear;
|
|
|
|
if (gear <= ARRAY_SIZE(data->pm_qos_int))
|
|
int_freq = data->pm_qos_int[gear - 1];
|
|
else
|
|
int_freq = 0;
|
|
|
|
return int_freq;
|
|
}
|
|
EXPORT_SYMBOL(abox_get_requiring_int_freq_in_khz);
|
|
|
|
unsigned int abox_get_requiring_aud_freq_in_khz(void)
|
|
{
|
|
struct abox_data *data = p_abox_data;
|
|
unsigned int gear;
|
|
unsigned int aud_freq;
|
|
|
|
if (data == NULL)
|
|
return 0;
|
|
|
|
gear = data->cpu_gear;
|
|
|
|
if (gear <= ARRAY_SIZE(data->pm_qos_aud))
|
|
aud_freq = data->pm_qos_aud[gear - 1];
|
|
else
|
|
aud_freq = 0;
|
|
|
|
return aud_freq;
|
|
}
|
|
EXPORT_SYMBOL(abox_get_requiring_aud_freq_in_khz);
|
|
|
|
bool abox_cpu_gear_idle(struct device *dev, struct abox_data *data,
|
|
unsigned int id)
|
|
{
|
|
struct abox_qos_request *request;
|
|
|
|
dev_dbg(dev, "%s(%x)\n", __func__, id);
|
|
|
|
for (request = data->cpu_gear_requests;
|
|
request - data->cpu_gear_requests <
|
|
ARRAY_SIZE(data->cpu_gear_requests)
|
|
&& request->id;
|
|
request++) {
|
|
if (id == request->id) {
|
|
if (request->value >= ABOX_CPU_GEAR_MIN)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void abox_check_cpu_gear(struct device *dev,
|
|
struct abox_data *data,
|
|
unsigned int old_id, unsigned int old_gear,
|
|
unsigned int id, unsigned int gear)
|
|
{
|
|
struct device *dev_abox = &data->pdev->dev;
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
if (id != ABOX_CPU_GEAR_BOOT)
|
|
return;
|
|
|
|
if (data->calliope_state == CALLIOPE_ENABLING)
|
|
abox_boot_done(dev_abox, data->calliope_version);
|
|
|
|
if (old_id != id) {
|
|
if (gear < ABOX_CPU_GEAR_MIN) {
|
|
/* new */
|
|
dev_dbg(dev, "%s(%x): new\n", __func__, id);
|
|
pm_wakeup_event(dev_abox, BOOT_DONE_TIMEOUT_MS);
|
|
__pm_stay_awake(&data->ws_boot);
|
|
abox_request_dram_on(pdev, (void *)BOOT_CPU_GEAR_ID, true);
|
|
}
|
|
} else {
|
|
if ((old_gear >= ABOX_CPU_GEAR_MIN) &&
|
|
(gear < ABOX_CPU_GEAR_MIN)) {
|
|
/* on */
|
|
dev_dbg(dev, "%s(%x): on\n", __func__, id);
|
|
pm_wakeup_event(dev_abox, BOOT_DONE_TIMEOUT_MS);
|
|
__pm_stay_awake(&data->ws_boot);
|
|
abox_request_dram_on(pdev, (void *)BOOT_CPU_GEAR_ID, true);
|
|
} else if ((old_gear < ABOX_CPU_GEAR_MIN) &&
|
|
(gear >= ABOX_CPU_GEAR_MIN)) {
|
|
/* off */
|
|
dev_dbg(dev, "%s(%x): off\n", __func__, id);
|
|
pm_relax(dev_abox);
|
|
abox_request_dram_on(pdev, (void *)BOOT_CPU_GEAR_ID, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void abox_notify_cpu_gear(struct abox_data *data, unsigned int freq, unsigned int clksrc_to_aclk)
|
|
{
|
|
const unsigned long long TIMER_RATE = 26000000;
|
|
struct device *dev = &data->pdev->dev;
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
unsigned long long ktime, atime;
|
|
unsigned long long time = sched_clock();
|
|
unsigned long rem = do_div(time, NSEC_PER_SEC);
|
|
|
|
switch (data->calliope_state) {
|
|
case CALLIOPE_ENABLING:
|
|
case CALLIOPE_ENABLED:
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_CHANGED_GEAR;
|
|
system_msg->param1 = (int)freq;
|
|
system_msg->param2 = (int)time; /* SEC */
|
|
system_msg->param3 = (int)rem; /* NSEC */
|
|
if (clksrc_to_aclk) {
|
|
ktime = sched_clock();
|
|
atime = readq_relaxed(data->sfr_base + ABOX_TIMER_CURVALUD_LSB(1));
|
|
/* clock to ns */
|
|
atime *= 500;
|
|
do_div(atime, TIMER_RATE / 2000000);
|
|
} else {
|
|
ktime = ULLONG_MAX;
|
|
atime = ULLONG_MAX;
|
|
}
|
|
|
|
system_msg->bundle.param_u64[0] = ktime;
|
|
system_msg->bundle.param_u64[1] = atime;
|
|
|
|
abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
break;
|
|
case CALLIOPE_DISABLING:
|
|
case CALLIOPE_DISABLED:
|
|
default:
|
|
/* notification to passing by context is not needed */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void abox_change_cpu_gear_legacy(struct device *dev,
|
|
struct abox_data *data)
|
|
{
|
|
struct abox_qos_request *request;
|
|
unsigned int gear = UINT_MAX;
|
|
int ret;
|
|
bool increasing;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->cpu_gear_requests;
|
|
request - data->cpu_gear_requests <
|
|
ARRAY_SIZE(data->cpu_gear_requests)
|
|
&& request->id;
|
|
request++) {
|
|
if (gear > request->value)
|
|
gear = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, gear=%u\n", request->id,
|
|
request->value, gear);
|
|
}
|
|
|
|
if (data->cpu_gear == gear)
|
|
goto skip;
|
|
|
|
increasing = (gear < data->cpu_gear);
|
|
data->cpu_gear = gear;
|
|
|
|
if (increasing) {
|
|
if (gear <= ARRAY_SIZE(data->pm_qos_int))
|
|
pm_qos_update_request(&abox_pm_qos_int,
|
|
data->pm_qos_int[gear - 1]);
|
|
else
|
|
pm_qos_update_request(&abox_pm_qos_int, 0);
|
|
}
|
|
|
|
if (gear >= ABOX_CPU_GEAR_MIN) {
|
|
ret = clk_set_rate(data->clk_pll, 0);
|
|
if (ret < 0)
|
|
dev_warn(dev, "setting pll clock to 0 is failed: %d\n",
|
|
ret);
|
|
dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
|
|
|
|
ret = clk_set_rate(data->clk_cpu, AUD_PLL_RATE_KHZ);
|
|
if (ret < 0)
|
|
dev_warn(dev, "setting cpu clock gear to %d is failed: %d\n",
|
|
gear, ret);
|
|
} else {
|
|
ret = clk_set_rate(data->clk_cpu, AUD_PLL_RATE_KHZ / gear);
|
|
if (ret < 0)
|
|
dev_warn(dev, "setting cpu clock gear to %d is failed: %d\n",
|
|
gear, ret);
|
|
|
|
if (clk_get_rate(data->clk_pll) <= AUD_PLL_RATE_HZ_BYPASS) {
|
|
ret = clk_set_rate(data->clk_pll,
|
|
AUD_PLL_RATE_HZ_FOR_48000);
|
|
if (ret < 0)
|
|
dev_warn(dev, "setting pll clock to 0 is failed: %d\n",
|
|
ret);
|
|
dev_info(dev, "pll clock: %lu\n",
|
|
clk_get_rate(data->clk_pll));
|
|
}
|
|
}
|
|
dev_info(dev, "cpu clock: %lukHz\n", clk_get_rate(data->clk_cpu));
|
|
|
|
if (!increasing) {
|
|
if (gear <= ARRAY_SIZE(data->pm_qos_int))
|
|
pm_qos_update_request(&abox_pm_qos_int,
|
|
data->pm_qos_int[gear - 1]);
|
|
else
|
|
pm_qos_update_request(&abox_pm_qos_int, 0);
|
|
}
|
|
skip:
|
|
abox_notify_cpu_gear(data, clk_get_rate(data->clk_cpu) * 1000, 0);
|
|
}
|
|
|
|
static void abox_change_cpu_gear(struct device *dev, struct abox_data *data)
|
|
{
|
|
struct abox_qos_request *request;
|
|
unsigned int gear = UINT_MAX;
|
|
unsigned int clksrc_to_aclk = 0;
|
|
s32 freq;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->cpu_gear_requests;
|
|
request - data->cpu_gear_requests <
|
|
ARRAY_SIZE(data->cpu_gear_requests)
|
|
&& request->id;
|
|
request++) {
|
|
if (gear > request->value)
|
|
gear = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, gear=%u\n", request->id,
|
|
request->value, gear);
|
|
}
|
|
|
|
if ((data->cpu_gear >= ABOX_CPU_GEAR_MIN) &&
|
|
(gear < ABOX_CPU_GEAR_MIN)) {
|
|
/* first cpu gear request */
|
|
clk_set_rate(data->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
|
|
dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
|
|
clksrc_to_aclk = 1;
|
|
pm_runtime_get(dev);
|
|
}
|
|
|
|
if (data->cpu_gear != gear) {
|
|
freq = (gear <= ARRAY_SIZE(data->pm_qos_aud)) ?
|
|
data->pm_qos_aud[gear - 1] : 0;
|
|
pm_qos_update_request(&abox_pm_qos_aud, freq);
|
|
}
|
|
|
|
dev_info(dev, "pm qos request aud: req=%dkHz ret=%dkHz\n", freq,
|
|
pm_qos_request(abox_pm_qos_aud.pm_qos_class));
|
|
abox_notify_cpu_gear(data,
|
|
pm_qos_request(abox_pm_qos_aud.pm_qos_class) * 1000,
|
|
clksrc_to_aclk);
|
|
|
|
if ((data->cpu_gear < ABOX_CPU_GEAR_MIN) &&
|
|
(gear >= ABOX_CPU_GEAR_MIN)) {
|
|
/* no more cpu gear request */
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
clk_set_rate(data->clk_pll, 0);
|
|
dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
|
|
}
|
|
|
|
data->cpu_gear = gear;
|
|
}
|
|
|
|
static void abox_change_cpu_gear_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_cpu_gear_work);
|
|
|
|
if (IS_ENABLED(CONFIG_SOC_EXYNOS8895))
|
|
abox_change_cpu_gear_legacy(&data->pdev->dev, data);
|
|
else
|
|
abox_change_cpu_gear(&data->pdev->dev, data);
|
|
}
|
|
|
|
int abox_request_cpu_gear(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int gear)
|
|
{
|
|
struct abox_qos_request *request;
|
|
unsigned int old_id, old_gear;
|
|
size_t len = ARRAY_SIZE(data->cpu_gear_requests);
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, gear);
|
|
|
|
for (request = data->cpu_gear_requests;
|
|
request - data->cpu_gear_requests < len
|
|
&& request->id && request->id != id;
|
|
request++) {
|
|
}
|
|
|
|
old_id = request->id;
|
|
old_gear = request->value;
|
|
request->value = gear;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->cpu_gear_requests >=
|
|
ARRAY_SIZE(data->cpu_gear_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, gear=%u\n", __func__,
|
|
id, gear);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
queue_work(data->gear_workqueue, &data->change_cpu_gear_work);
|
|
abox_check_cpu_gear(dev, data, old_id, old_gear, id, gear);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void abox_cpu_gear_barrier(struct abox_data *data)
|
|
{
|
|
flush_work(&data->change_cpu_gear_work);
|
|
}
|
|
|
|
int abox_request_cpu_gear_sync(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int gear)
|
|
{
|
|
int ret = abox_request_cpu_gear(dev, data, id, gear);
|
|
|
|
abox_cpu_gear_barrier(data);
|
|
return ret;
|
|
}
|
|
|
|
void abox_clear_cpu_gear_requests(struct device *dev, struct abox_data *data)
|
|
{
|
|
struct abox_qos_request *req;
|
|
size_t len = ARRAY_SIZE(data->cpu_gear_requests);
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
for (req = data->cpu_gear_requests; req - data->cpu_gear_requests < len
|
|
&& req->id; req++) {
|
|
if (req->value < ABOX_CPU_GEAR_MIN)
|
|
abox_request_cpu_gear(dev, data, req->id,
|
|
ABOX_CPU_GEAR_MIN);
|
|
}
|
|
}
|
|
|
|
static void abox_change_int_freq_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_int_freq_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
struct abox_qos_request *request;
|
|
unsigned int freq = 0;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->int_requests; request - data->int_requests <
|
|
ARRAY_SIZE(data->int_requests) && request->id;
|
|
request++) {
|
|
if (freq < request->value)
|
|
freq = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, freq=%u\n", request->id,
|
|
request->value, freq);
|
|
}
|
|
|
|
data->int_freq = freq;
|
|
pm_qos_update_request(&abox_pm_qos_int, data->int_freq);
|
|
|
|
dev_info(dev, "pm qos request int: %dHz\n", pm_qos_request(
|
|
abox_pm_qos_int.pm_qos_class));
|
|
}
|
|
|
|
int abox_request_int_freq(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int int_freq)
|
|
{
|
|
struct abox_qos_request *request;
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, int_freq);
|
|
|
|
if (!id)
|
|
id = DEFAULT_INT_FREQ_ID;
|
|
|
|
for (request = data->int_requests; request - data->int_requests <
|
|
ARRAY_SIZE(data->int_requests) && request->id &&
|
|
request->id != id; request++) {
|
|
}
|
|
|
|
request->value = int_freq;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->int_requests >= ARRAY_SIZE(data->int_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, int_freq=%u\n", __func__,
|
|
id, int_freq);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->change_int_freq_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_change_mif_freq_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_mif_freq_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
struct abox_qos_request *request;
|
|
unsigned int freq = 0;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->mif_requests; request - data->mif_requests <
|
|
ARRAY_SIZE(data->mif_requests) && request->id;
|
|
request++) {
|
|
if (freq < request->value)
|
|
freq = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, freq=%u\n", request->id,
|
|
request->value, freq);
|
|
}
|
|
|
|
data->mif_freq = freq;
|
|
pm_qos_update_request(&abox_pm_qos_mif, data->mif_freq);
|
|
|
|
dev_info(dev, "pm qos request mif: %dHz\n", pm_qos_request(
|
|
abox_pm_qos_mif.pm_qos_class));
|
|
}
|
|
|
|
static int abox_request_mif_freq(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int mif_freq)
|
|
{
|
|
struct abox_qos_request *request;
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, mif_freq);
|
|
|
|
if (!id)
|
|
id = DEFAULT_MIF_FREQ_ID;
|
|
|
|
for (request = data->mif_requests; request - data->mif_requests <
|
|
ARRAY_SIZE(data->mif_requests) && request->id &&
|
|
request->id != id; request++) {
|
|
}
|
|
|
|
request->value = mif_freq;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->mif_requests >= ARRAY_SIZE(data->mif_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, mif_freq=%u\n", __func__,
|
|
id, mif_freq);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->change_mif_freq_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_change_lit_freq_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_lit_freq_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
size_t array_size = ARRAY_SIZE(data->lit_requests);
|
|
struct abox_qos_request *request;
|
|
unsigned int freq = 0;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->lit_requests;
|
|
request - data->lit_requests < array_size &&
|
|
request->id; request++) {
|
|
if (freq < request->value)
|
|
freq = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, freq=%u\n", request->id,
|
|
request->value, freq);
|
|
}
|
|
|
|
data->lit_freq = freq;
|
|
pm_qos_update_request(&abox_pm_qos_lit, data->lit_freq);
|
|
|
|
dev_info(dev, "pm qos request little: %dkHz\n",
|
|
pm_qos_request(abox_pm_qos_lit.pm_qos_class));
|
|
}
|
|
|
|
int abox_request_lit_freq(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int freq)
|
|
{
|
|
size_t array_size = ARRAY_SIZE(data->lit_requests);
|
|
struct abox_qos_request *request;
|
|
|
|
if (!id)
|
|
id = DEFAULT_LIT_FREQ_ID;
|
|
|
|
for (request = data->lit_requests;
|
|
request - data->lit_requests < array_size &&
|
|
request->id && request->id != id; request++) {
|
|
}
|
|
|
|
if ((request->id == id) && (request->value == freq))
|
|
return 0;
|
|
|
|
request->value = freq;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, freq);
|
|
|
|
if (request - data->lit_requests >= ARRAY_SIZE(data->lit_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, freq=%u\n",
|
|
__func__, id, freq);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->change_lit_freq_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_change_big_freq_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_big_freq_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
size_t array_size = ARRAY_SIZE(data->big_requests);
|
|
struct abox_qos_request *request;
|
|
unsigned int freq = 0;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->big_requests;
|
|
request - data->big_requests < array_size &&
|
|
request->id; request++) {
|
|
if (freq < request->value)
|
|
freq = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, freq=%u\n", request->id,
|
|
request->value, freq);
|
|
}
|
|
|
|
data->big_freq = freq;
|
|
pm_qos_update_request(&abox_pm_qos_big, data->big_freq);
|
|
|
|
dev_info(dev, "pm qos request big: %dkHz\n",
|
|
pm_qos_request(abox_pm_qos_big.pm_qos_class));
|
|
}
|
|
|
|
int abox_request_big_freq(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int freq)
|
|
{
|
|
size_t array_size = ARRAY_SIZE(data->big_requests);
|
|
struct abox_qos_request *request;
|
|
|
|
if (!id)
|
|
id = DEFAULT_BIG_FREQ_ID;
|
|
|
|
for (request = data->big_requests;
|
|
request - data->big_requests < array_size &&
|
|
request->id && request->id != id; request++) {
|
|
}
|
|
|
|
if ((request->id == id) && (request->value == freq))
|
|
return 0;
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, freq);
|
|
|
|
request->value = freq;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->big_requests >= ARRAY_SIZE(data->big_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, freq=%u\n",
|
|
__func__, id, freq);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->change_big_freq_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_change_hmp_boost_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
change_hmp_boost_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
size_t array_size = ARRAY_SIZE(data->hmp_requests);
|
|
struct abox_qos_request *request;
|
|
unsigned int on = 0;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->hmp_requests;
|
|
request - data->hmp_requests < array_size &&
|
|
request->id; request++) {
|
|
if (request->value)
|
|
on = request->value;
|
|
|
|
dev_dbg(dev, "id=%x, value=%u, on=%u\n", request->id,
|
|
request->value, on);
|
|
}
|
|
|
|
if (data->hmp_boost != on) {
|
|
dev_info(dev, "request hmp boost: %d\n", on);
|
|
|
|
data->hmp_boost = on;
|
|
#ifdef CONFIG_SCHED_HMP
|
|
set_hmp_boost(on);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int abox_request_hmp_boost(struct device *dev, struct abox_data *data,
|
|
unsigned int id, unsigned int on)
|
|
{
|
|
size_t array_size = ARRAY_SIZE(data->hmp_requests);
|
|
struct abox_qos_request *request;
|
|
|
|
if (!id)
|
|
id = DEFAULT_HMP_BOOST_ID;
|
|
|
|
for (request = data->hmp_requests;
|
|
request - data->hmp_requests < array_size &&
|
|
request->id && request->id != id; request++) {
|
|
}
|
|
|
|
if ((request->id == id) && (request->value == on))
|
|
return 0;
|
|
|
|
dev_info(dev, "%s(%x, %u)\n", __func__, id, on);
|
|
|
|
request->value = on;
|
|
wmb(); /* value is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->hmp_requests >= ARRAY_SIZE(data->hmp_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%x, on=%u\n",
|
|
__func__, id, on);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->change_hmp_boost_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void abox_request_dram_on(struct platform_device *pdev_abox, void *id, bool on)
|
|
{
|
|
struct device *dev = &pdev_abox->dev;
|
|
struct abox_data *data = platform_get_drvdata(pdev_abox);
|
|
struct abox_dram_request *request;
|
|
unsigned int val = 0x0;
|
|
|
|
dev_dbg(dev, "%s(%d)\n", __func__, on);
|
|
|
|
for (request = data->dram_requests; request - data->dram_requests <
|
|
ARRAY_SIZE(data->dram_requests) && request->id &&
|
|
request->id != id; request++) {
|
|
}
|
|
|
|
request->on = on;
|
|
wmb(); /* on is read after id in reading function */
|
|
request->id = id;
|
|
|
|
for (request = data->dram_requests; request - data->dram_requests <
|
|
ARRAY_SIZE(data->dram_requests) && request->id;
|
|
request++) {
|
|
if (request->on) {
|
|
val = ABOX_SYSPOWER_CTRL_MASK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
regmap_write(data->regmap, ABOX_SYSPOWER_CTRL, val);
|
|
dev_dbg(dev, "%s: SYSPOWER_CTRL=%08x\n", __func__,
|
|
({regmap_read(data->regmap, ABOX_SYSPOWER_CTRL, &val);
|
|
val; }));
|
|
}
|
|
|
|
int abox_iommu_map(struct device *dev, unsigned long iova,
|
|
phys_addr_t addr, size_t bytes, void *area)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct abox_iommu_mapping *mapping;
|
|
int ret;
|
|
|
|
dev_info(dev, "%s(%#lx, %pa, %#zx, %p)\n", __func__,
|
|
iova, &addr, bytes, area);
|
|
|
|
mutex_lock(&data->iommu_lock);
|
|
ret = iommu_map(data->iommu_domain, iova, addr, bytes, 0);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to iommu_map: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
mapping = devm_kmalloc(dev, sizeof(*mapping), GFP_KERNEL);
|
|
if (!mapping)
|
|
return -ENOMEM;
|
|
|
|
if (!area) {
|
|
dev_warn(dev, "%s: no virtual address\n", __func__);
|
|
area = phys_to_virt(addr);
|
|
}
|
|
|
|
mapping->iova = iova;
|
|
mapping->addr = addr;
|
|
mapping->area = area;
|
|
mapping->bytes = bytes;
|
|
list_add_rcu(&mapping->list, &data->iommu_maps);
|
|
mutex_unlock(&data->iommu_lock);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(abox_iommu_map);
|
|
|
|
int abox_iommu_unmap(struct device *dev, unsigned long iova)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct abox_iommu_mapping *mapping;
|
|
size_t bytes;
|
|
int ret = 0;
|
|
|
|
dev_info(dev, "%s(%#lx)\n", __func__, iova);
|
|
|
|
mutex_lock(&data->iommu_lock);
|
|
list_for_each_entry(mapping, &data->iommu_maps, list) {
|
|
if (iova != mapping->iova)
|
|
continue;
|
|
|
|
bytes = mapping->bytes;
|
|
ret = iommu_unmap(data->iommu_domain, iova, bytes);
|
|
if (ret < 0)
|
|
dev_err(dev, "Failed to iommu_unmap: %d\n", ret);
|
|
|
|
exynos_sysmmu_tlb_invalidate(data->iommu_domain, iova, bytes);
|
|
list_del_rcu(&mapping->list);
|
|
synchronize_rcu();
|
|
devm_kfree(dev, mapping);
|
|
break;
|
|
}
|
|
mutex_unlock(&data->iommu_lock);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(abox_iommu_unmap);
|
|
|
|
int abox_iommu_map_sg(struct device *dev, unsigned long iova,
|
|
struct scatterlist *sg, unsigned int nents,
|
|
int prot, size_t bytes, void *area)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%#lx)\n", __func__, iova);
|
|
|
|
ret = iommu_map_sg(data->iommu_domain, iova, sg, nents, prot);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to iommu_map_sg(%#lx): %d\n", iova, ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(abox_iommu_map_sg);
|
|
|
|
int abox_register_irq_handler(struct device *dev, int ipc_id,
|
|
abox_irq_handler_t irq_handler, void *dev_id)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct abox_irq_action *irq_action = NULL;
|
|
bool new_handler = true;
|
|
|
|
if (ipc_id >= IPC_ID_COUNT)
|
|
return -EINVAL;
|
|
|
|
list_for_each_entry(irq_action, &data->irq_actions, list) {
|
|
if (irq_action->irq == ipc_id && irq_action->dev_id == dev_id) {
|
|
new_handler = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (new_handler) {
|
|
irq_action = devm_kzalloc(dev, sizeof(struct abox_irq_action),
|
|
GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(irq_action)) {
|
|
dev_err(dev, "%s: kmalloc fail\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
irq_action->irq = ipc_id;
|
|
irq_action->dev_id = dev_id;
|
|
list_add_tail(&irq_action->list, &data->irq_actions);
|
|
}
|
|
|
|
irq_action->irq_handler = irq_handler;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(abox_register_irq_handler);
|
|
|
|
static int abox_control_asrc(struct snd_soc_dapm_widget *w, bool on)
|
|
{
|
|
struct snd_kcontrol *kcontrol;
|
|
struct snd_soc_component *cmpnt;
|
|
struct soc_enum *e;
|
|
unsigned int reg, mask, val;
|
|
|
|
if (!w || !w->name || !w->num_kcontrols)
|
|
return -EINVAL;
|
|
|
|
kcontrol = w->kcontrols[0];
|
|
cmpnt = w->dapm->component;
|
|
e = (struct soc_enum *)kcontrol->private_value;
|
|
reg = e->reg;
|
|
mask = e->mask << e->shift_l;
|
|
val = (on ? 1 : 0) << e->shift_l;
|
|
|
|
return snd_soc_component_update_bits(cmpnt, reg, mask, val);
|
|
}
|
|
|
|
static bool abox_is_asrc_widget(struct snd_soc_dapm_widget *w)
|
|
{
|
|
return w->name && !!strstr(w->name, "ASRC");
|
|
}
|
|
|
|
int abox_try_to_asrc_off(struct device *dev, struct abox_data *data,
|
|
struct snd_soc_pcm_runtime *fe, int stream)
|
|
{
|
|
struct snd_soc_dai *dai = fe->cpu_dai;
|
|
struct snd_soc_component *cmpnt = dai->component;
|
|
struct snd_soc_dapm_widget *w, *w_asrc = NULL;
|
|
LIST_HEAD(widget_list);
|
|
enum ABOX_CONFIGMSG rate, format;
|
|
unsigned int out_rate = 0, out_width = 0;
|
|
unsigned int pcm_rate, pcm_width;
|
|
bool slave;
|
|
|
|
if (!abox_test_quirk(data, ABOX_QUIRK_TRY_TO_ASRC_OFF))
|
|
return 0;
|
|
|
|
dev_dbg(dev, "%s(%s)\n", __func__, dai->name);
|
|
|
|
pcm_rate = params_rate(&fe->dpcm[stream].hw_params);
|
|
pcm_width = params_width(&fe->dpcm[stream].hw_params);
|
|
|
|
snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(cmpnt));
|
|
/*
|
|
* For snd_soc_dapm_connected_{output,input}_ep fully discover the graph
|
|
* we need to reset the cached number of inputs and outputs.
|
|
*/
|
|
list_for_each_entry(w, &cmpnt->card->widgets, list) {
|
|
w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
|
|
w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
|
|
}
|
|
if (dai->playback_widget)
|
|
snd_soc_dapm_connected_output_ep(dai->playback_widget,
|
|
&widget_list);
|
|
if (dai->capture_widget)
|
|
snd_soc_dapm_connected_input_ep(dai->capture_widget,
|
|
&widget_list);
|
|
|
|
list_for_each_entry(w, &widget_list, work_list) {
|
|
dev_dbg(dev, "%s", w->name);
|
|
|
|
if (abox_find_nrf_stream(w, stream, &rate, &format, &slave)) {
|
|
out_rate = abox_get_sif_rate(data, rate);
|
|
out_width = abox_get_sif_width(data, format);
|
|
dev_dbg(dev, "%s: rate=%u, width=%u\n",
|
|
w->name, out_rate, out_width);
|
|
}
|
|
|
|
if (abox_is_asrc_widget(w)) {
|
|
w_asrc = w;
|
|
dev_dbg(dev, "%s is asrc\n", w->name);
|
|
}
|
|
|
|
if (w_asrc && out_rate && out_width)
|
|
break;
|
|
}
|
|
snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(cmpnt));
|
|
|
|
if (!w_asrc || !out_rate || !out_width) {
|
|
dev_warn(dev, "%s: incomplete path: w_asrc=%s, out_rate=%u, out_width=%u",
|
|
__func__, w_asrc ? w_asrc->name : "(null)",
|
|
out_rate, out_width);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return abox_control_asrc(w_asrc, (pcm_rate != out_rate) ||
|
|
(pcm_width != out_width));
|
|
}
|
|
|
|
static int abox_register_if_routes(struct device *dev,
|
|
const struct snd_soc_dapm_route *route_base, int num,
|
|
struct snd_soc_dapm_context *dapm, const char *name)
|
|
{
|
|
struct snd_soc_dapm_route *route;
|
|
int i;
|
|
|
|
route = devm_kmemdup(dev, route_base, sizeof(*route_base) * num,
|
|
GFP_KERNEL);
|
|
if (!route) {
|
|
dev_err(dev, "%s: insufficient memory\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (route[i].sink)
|
|
route[i].sink = devm_kasprintf(dev, GFP_KERNEL,
|
|
route[i].sink, name);
|
|
if (route[i].control)
|
|
route[i].control = devm_kasprintf(dev, GFP_KERNEL,
|
|
route[i].control, name);
|
|
if (route[i].source)
|
|
route[i].source = devm_kasprintf(dev, GFP_KERNEL,
|
|
route[i].source, name);
|
|
}
|
|
|
|
snd_soc_dapm_add_routes(dapm, route, num);
|
|
devm_kfree(dev, route);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int abox_register_if(struct platform_device *pdev_abox,
|
|
struct platform_device *pdev_if, unsigned int id,
|
|
struct snd_soc_dapm_context *dapm, const char *name,
|
|
bool playback, bool capture)
|
|
{
|
|
struct device *dev = &pdev_if->dev;
|
|
struct abox_data *data = platform_get_drvdata(pdev_abox);
|
|
int ret;
|
|
|
|
static const struct snd_soc_dapm_route route_base_pla[] = {
|
|
/* sink, control, source */
|
|
{"%s Playback", NULL, "%s SPK"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route route_base_cap[] = {
|
|
/* sink, control, source */
|
|
{"SPUSM", "%s", "%s Capture"},
|
|
{"NSRC0", "%s", "%s Capture"},
|
|
{"NSRC1", "%s", "%s Capture"},
|
|
{"NSRC2", "%s", "%s Capture"},
|
|
{"NSRC3", "%s", "%s Capture"},
|
|
};
|
|
|
|
if (id >= ARRAY_SIZE(data->pdev_if)) {
|
|
dev_err(dev, "%s: invalid id(%u)\n", __func__, id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data->cmpnt->name_prefix && dapm->component->name_prefix &&
|
|
strcmp(data->cmpnt->name_prefix,
|
|
dapm->component->name_prefix)) {
|
|
dev_err(dev, "%s: name prefix is different: %s != %s\n",
|
|
__func__, data->cmpnt->name_prefix,
|
|
dapm->component->name_prefix);
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->pdev_if[id] = pdev_if;
|
|
if (id > data->if_count)
|
|
data->if_count = id + 1;
|
|
|
|
if (playback) {
|
|
ret = abox_register_if_routes(dev, route_base_pla,
|
|
ARRAY_SIZE(route_base_pla), dapm, name);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
if (capture) {
|
|
ret = abox_register_if_routes(dev, route_base_cap,
|
|
ARRAY_SIZE(route_base_cap), dapm, name);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int abox_register_rdma(struct platform_device *pdev_abox,
|
|
struct platform_device *pdev_rdma, unsigned int id)
|
|
{
|
|
struct abox_data *data = platform_get_drvdata(pdev_abox);
|
|
|
|
if (id < ARRAY_SIZE(data->pdev_rdma)) {
|
|
data->pdev_rdma[id] = pdev_rdma;
|
|
if (id > data->rdma_count)
|
|
data->rdma_count = id + 1;
|
|
} else {
|
|
dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int abox_register_wdma(struct platform_device *pdev_abox,
|
|
struct platform_device *pdev_wdma, unsigned int id)
|
|
{
|
|
struct abox_data *data = platform_get_drvdata(pdev_abox);
|
|
|
|
if (id < ARRAY_SIZE(data->pdev_wdma)) {
|
|
data->pdev_wdma[id] = pdev_wdma;
|
|
if (id > data->wdma_count)
|
|
data->wdma_count = id + 1;
|
|
} else {
|
|
dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_component_control_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_component_kcontrol_value *value =
|
|
(void *)kcontrol->private_value;
|
|
|
|
dev_dbg(dev, "%s(%s)\n", __func__, kcontrol->id.name);
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = value->control->count;
|
|
uinfo->value.integer.min = value->control->min;
|
|
uinfo->value.integer.max = value->control->max;
|
|
return 0;
|
|
}
|
|
|
|
static ABOX_IPC_MSG abox_component_control_get_msg;
|
|
|
|
static int abox_component_control_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct abox_component_kcontrol_value *value =
|
|
(void *)kcontrol->private_value;
|
|
ABOX_IPC_MSG *msg = &abox_component_control_get_msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg->msg.system;
|
|
int i, ret;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (value->cache_only) {
|
|
for (i = 0; i < value->control->count; i++)
|
|
ucontrol->value.integer.value[i] = value->cache[i];
|
|
return 0;
|
|
}
|
|
|
|
msg->ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_REQUEST_COMPONENT_CONTROL;
|
|
system_msg->param1 = value->desc->id;
|
|
system_msg->param2 = value->control->id;
|
|
ret = abox_request_ipc(dev, msg->ipcid, msg, sizeof(*msg), 0, 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = wait_event_timeout(data->ipc_wait_queue,
|
|
system_msg->msgtype == ABOX_REPORT_COMPONENT_CONTROL,
|
|
msecs_to_jiffies(1000));
|
|
if (system_msg->msgtype != ABOX_REPORT_COMPONENT_CONTROL)
|
|
return -ETIME;
|
|
|
|
for (i = 0; i < value->control->count; i++) {
|
|
long val = (long)system_msg->bundle.param_s32[i];
|
|
|
|
ucontrol->value.integer.value[i] = val;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_component_control_put_ipc(struct device *dev,
|
|
struct abox_component_kcontrol_value *value)
|
|
{
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
int i;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (i = 0; i < value->control->count; i++) {
|
|
int val = value->cache[i];
|
|
char *name = value->control->name;
|
|
|
|
system_msg->bundle.param_s32[i] = val;
|
|
dev_dbg(dev, "%s: %s[%d] <= %d", __func__, name, i, val);
|
|
}
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_UPDATE_COMPONENT_CONTROL;
|
|
system_msg->param1 = value->desc->id;
|
|
system_msg->param2 = value->control->id;
|
|
|
|
return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
}
|
|
|
|
static int abox_component_control_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
|
struct device *dev = cmpnt->dev;
|
|
struct abox_component_kcontrol_value *value =
|
|
(void *)kcontrol->private_value;
|
|
int i;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (i = 0; i < value->control->count; i++) {
|
|
int val = (int)ucontrol->value.integer.value[i];
|
|
char *name = kcontrol->id.name;
|
|
|
|
value->cache[i] = val;
|
|
dev_dbg(dev, "%s: %s[%d] <= %d", __func__, name, i, val);
|
|
}
|
|
|
|
return abox_component_control_put_ipc(dev, value);
|
|
}
|
|
|
|
#define ABOX_COMPONENT_KCONTROL(xname, xdesc, xcontrol) \
|
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
|
.info = abox_component_control_info, \
|
|
.get = abox_component_control_get, \
|
|
.put = abox_component_control_put, \
|
|
.private_value = \
|
|
(unsigned long)&(struct abox_component_kcontrol_value) \
|
|
{.desc = xdesc, .control = xcontrol} }
|
|
|
|
struct snd_kcontrol_new abox_component_kcontrols[] = {
|
|
ABOX_COMPONENT_KCONTROL(NULL, NULL, NULL),
|
|
};
|
|
|
|
static void abox_register_component_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
register_component_work);
|
|
struct device *dev = &data->pdev->dev;
|
|
struct abox_component *component;
|
|
int i;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (component = data->components; ((component - data->components) <
|
|
ARRAY_SIZE(data->components)); component++) {
|
|
struct ABOX_COMPONENT_DESCRIPTIOR *desc = component->desc;
|
|
|
|
if (!component->desc || component->registered)
|
|
continue;
|
|
|
|
for (i = 0; i < desc->control_count; i++) {
|
|
struct ABOX_COMPONENT_CONTROL *control =
|
|
&desc->controls[i];
|
|
struct abox_component_kcontrol_value *value;
|
|
char kcontrol_name[64];
|
|
|
|
value = devm_kzalloc(dev, sizeof(*value) +
|
|
(control->count *
|
|
sizeof(value->cache[0])), GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(value)) {
|
|
dev_err(dev, "%s: kmalloc fail\n", __func__);
|
|
continue;
|
|
}
|
|
value->desc = desc;
|
|
value->control = control;
|
|
list_add_tail(&value->list, &component->value_list);
|
|
|
|
snprintf(kcontrol_name, sizeof(kcontrol_name), "%s %s",
|
|
desc->name, control->name);
|
|
|
|
abox_component_kcontrols[0].name = devm_kstrdup(dev,
|
|
kcontrol_name, GFP_KERNEL);
|
|
abox_component_kcontrols[0].private_value =
|
|
(unsigned long)value;
|
|
if (data->cmpnt) {
|
|
snd_soc_add_component_controls(data->cmpnt,
|
|
abox_component_kcontrols, 1);
|
|
}
|
|
}
|
|
component->registered = true;
|
|
}
|
|
}
|
|
|
|
|
|
static int abox_register_component(struct device *dev,
|
|
struct ABOX_COMPONENT_DESCRIPTIOR *desc)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct abox_component *component;
|
|
|
|
dev_dbg(dev, "%s(%d, %s)\n", __func__, desc->id, desc->name);
|
|
|
|
for (component = data->components;
|
|
((component - data->components) <
|
|
ARRAY_SIZE(data->components)) &&
|
|
component->desc && component->desc != desc;
|
|
component++) {
|
|
}
|
|
|
|
if (!component->desc) {
|
|
component->desc = desc;
|
|
INIT_LIST_HEAD(&component->value_list);
|
|
schedule_work(&data->register_component_work);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_restore_component_control(struct device *dev,
|
|
struct abox_component_kcontrol_value *value)
|
|
{
|
|
int count = value->control->count;
|
|
int *val;
|
|
|
|
for (val = value->cache; val - value->cache < count; val++) {
|
|
if (*val) {
|
|
abox_component_control_put_ipc(dev, value);
|
|
break;
|
|
}
|
|
}
|
|
value->cache_only = false;
|
|
}
|
|
|
|
static void abox_restore_components(struct device *dev, struct abox_data *data)
|
|
{
|
|
struct abox_component *component;
|
|
struct abox_component_kcontrol_value *value;
|
|
size_t len = ARRAY_SIZE(data->components);
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
for (component = data->components; component - data->components < len;
|
|
component++) {
|
|
if (!component->registered)
|
|
break;
|
|
list_for_each_entry(value, &component->value_list, list) {
|
|
abox_restore_component_control(dev, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void abox_cache_components(struct device *dev, struct abox_data *data)
|
|
{
|
|
struct abox_component *component;
|
|
struct abox_component_kcontrol_value *value;
|
|
size_t len = ARRAY_SIZE(data->components);
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (component = data->components; component - data->components < len;
|
|
component++) {
|
|
if (!component->registered)
|
|
break;
|
|
list_for_each_entry(value, &component->value_list, list) {
|
|
value->cache_only = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool abox_is_calliope_incompatible(struct device *dev)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
|
|
memcpy(&msg, data->sram_base + 0x30040, 0x3C);
|
|
|
|
return ((system_msg->param3 >> 24) == 'A');
|
|
}
|
|
|
|
static void abox_restore_data(struct device *dev)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
int i;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
for (i = SET_MIXER_SAMPLE_RATE; i <= SET_INMUX4_SAMPLE_RATE; i++)
|
|
abox_sample_rate_put_ipc(dev,
|
|
data->sif_rate[abox_sif_idx(i)], i);
|
|
for (i = SET_MIXER_FORMAT; i <= SET_INMUX4_FORMAT; i++)
|
|
abox_sif_format_put_ipc(dev,
|
|
data->sif_format[abox_sif_idx(i)],
|
|
data->sif_channels[abox_sif_idx(i)], i);
|
|
if (data->erap_status[ERAP_ECHO_CANCEL])
|
|
abox_erap_handler_put_ipc(dev, ERAP_ECHO_CANCEL,
|
|
data->erap_status[ERAP_ECHO_CANCEL]);
|
|
if (data->erap_status[ERAP_VI_SENSE])
|
|
abox_erap_handler_put_ipc(dev, ERAP_VI_SENSE,
|
|
data->erap_status[ERAP_VI_SENSE]);
|
|
if (data->audio_mode)
|
|
abox_audio_mode_put_ipc(dev, data->audio_mode);
|
|
if (data->sound_type)
|
|
abox_sound_type_put_ipc(dev, data->sound_type);
|
|
abox_restore_components(dev, data);
|
|
abox_effect_restore();
|
|
}
|
|
|
|
static void abox_boot_done_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data,
|
|
boot_done_work);
|
|
struct platform_device *pdev = data->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
abox_cpu_pm_ipc(data, true);
|
|
abox_restore_data(dev);
|
|
abox_request_cpu_gear(dev, data, DEFAULT_CPU_GEAR_ID,
|
|
ABOX_CPU_GEAR_MIN);
|
|
|
|
__pm_relax(&data->ws_boot);
|
|
dev_info(dev, "%s:release wake lock\n", __func__);
|
|
}
|
|
|
|
static void abox_boot_done(struct device *dev, unsigned int version)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
char ver_char[4];
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
data->calliope_version = version;
|
|
memcpy(ver_char, &version, sizeof(ver_char));
|
|
dev_info(dev, "Calliope is ready to sing (version:%c%c%c%c)\n",
|
|
ver_char[3], ver_char[2], ver_char[1], ver_char[0]);
|
|
data->calliope_state = CALLIOPE_ENABLED;
|
|
schedule_work(&data->boot_done_work);
|
|
|
|
wake_up(&data->ipc_wait_queue);
|
|
}
|
|
|
|
static irqreturn_t abox_dma_irq_handler(int irq, struct abox_data *data)
|
|
{
|
|
struct device *dev = &data->pdev->dev;
|
|
int id;
|
|
struct platform_device **pdev_dma;
|
|
struct abox_platform_data *platform_data;
|
|
|
|
dev_dbg(dev, "%s(%d)\n", __func__, irq);
|
|
|
|
switch (irq) {
|
|
case RDMA0_BUF_EMPTY:
|
|
id = 0;
|
|
pdev_dma = data->pdev_rdma;
|
|
break;
|
|
case RDMA1_BUF_EMPTY:
|
|
id = 1;
|
|
pdev_dma = data->pdev_rdma;
|
|
break;
|
|
case RDMA2_BUF_EMPTY:
|
|
id = 2;
|
|
pdev_dma = data->pdev_rdma;
|
|
break;
|
|
case RDMA3_BUF_EMPTY:
|
|
id = 3;
|
|
pdev_dma = data->pdev_rdma;
|
|
break;
|
|
case WDMA0_BUF_FULL:
|
|
id = 0;
|
|
pdev_dma = data->pdev_wdma;
|
|
break;
|
|
case WDMA1_BUF_FULL:
|
|
id = 1;
|
|
pdev_dma = data->pdev_wdma;
|
|
break;
|
|
default:
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
if (unlikely(!pdev_dma[id])) {
|
|
dev_err(dev, "spurious dma irq: irq=%d id=%d\n", irq, id);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
platform_data = platform_get_drvdata(pdev_dma[id]);
|
|
if (unlikely(!platform_data)) {
|
|
dev_err(dev, "dma irq with null data: irq=%d id=%d\n", irq, id);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
platform_data->pointer = 0;
|
|
snd_pcm_period_elapsed(platform_data->substream);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t abox_registered_ipc_handler(struct device *dev, int irq,
|
|
struct abox_data *data, ABOX_IPC_MSG *msg, bool broadcast)
|
|
{
|
|
struct abox_irq_action *action;
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
dev_dbg(dev, "%s: irq=%d\n", __func__, irq);
|
|
|
|
list_for_each_entry(action, &data->irq_actions, list) {
|
|
if (action->irq != irq)
|
|
continue;
|
|
|
|
ret = action->irq_handler(irq, action->dev_id, msg);
|
|
if (!broadcast && ret == IRQ_HANDLED)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void abox_system_ipc_handler(struct device *dev,
|
|
struct abox_data *data, ABOX_IPC_MSG *msg)
|
|
{
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg->msg.system;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "msgtype=%d\n", system_msg->msgtype);
|
|
|
|
switch (system_msg->msgtype) {
|
|
case ABOX_BOOT_DONE:
|
|
if (abox_is_calliope_incompatible(dev))
|
|
dev_err(dev, "Calliope is not compatible with the driver\n");
|
|
|
|
abox_boot_done(dev, system_msg->param3);
|
|
abox_registered_ipc_handler(dev, IPC_SYSTEM, data, msg, true);
|
|
break;
|
|
case ABOX_CHANGE_GEAR:
|
|
abox_request_cpu_gear(dev, data, system_msg->param2,
|
|
system_msg->param1);
|
|
break;
|
|
case ABOX_END_L2C_CONTROL:
|
|
data->l2c_controlled = true;
|
|
wake_up(&data->ipc_wait_queue);
|
|
break;
|
|
case ABOX_REQUEST_L2C:
|
|
{
|
|
void *id = (void *)(long)system_msg->param2;
|
|
bool on = !!system_msg->param1;
|
|
|
|
abox_request_l2c(dev, data, id, on);
|
|
break;
|
|
}
|
|
case ABOX_REQUEST_SYSCLK:
|
|
switch (system_msg->param2) {
|
|
default:
|
|
/* fall through */
|
|
case 0:
|
|
abox_request_mif_freq(dev, data, system_msg->param3,
|
|
system_msg->param1);
|
|
break;
|
|
case 1:
|
|
abox_request_int_freq(dev, data, system_msg->param3,
|
|
system_msg->param1);
|
|
break;
|
|
}
|
|
break;
|
|
case ABOX_REPORT_LOG:
|
|
ret = abox_log_register_buffer(dev, system_msg->param1,
|
|
abox_addr_to_kernel_addr(data,
|
|
system_msg->param2));
|
|
if (ret < 0) {
|
|
dev_err(dev, "log buffer registration failed: %u, %u\n",
|
|
system_msg->param1, system_msg->param2);
|
|
}
|
|
break;
|
|
case ABOX_FLUSH_LOG:
|
|
break;
|
|
case ABOX_REPORT_DUMP:
|
|
ret = abox_dump_register_buffer(dev, system_msg->param1,
|
|
system_msg->bundle.param_bundle,
|
|
abox_addr_to_kernel_addr(data,
|
|
system_msg->param2),
|
|
abox_addr_to_phys_addr(data,
|
|
system_msg->param2),
|
|
system_msg->param3);
|
|
if (ret < 0) {
|
|
dev_err(dev, "dump buffer registration failed: %u, %u\n",
|
|
system_msg->param1, system_msg->param2);
|
|
}
|
|
break;
|
|
case ABOX_FLUSH_DUMP:
|
|
abox_dump_period_elapsed(system_msg->param1,
|
|
system_msg->param2);
|
|
break;
|
|
case ABOX_REPORT_COMPONENT:
|
|
abox_register_component(dev,
|
|
abox_addr_to_kernel_addr(data,
|
|
system_msg->param1));
|
|
break;
|
|
case ABOX_REPORT_COMPONENT_CONTROL:
|
|
abox_component_control_get_msg = *msg;
|
|
wake_up(&data->ipc_wait_queue);
|
|
break;
|
|
case ABOX_REPORT_FAULT:
|
|
{
|
|
const char *type;
|
|
|
|
switch (system_msg->param1) {
|
|
case 1:
|
|
type = "data abort";
|
|
break;
|
|
case 2:
|
|
type = "prefetch abort";
|
|
break;
|
|
case 3:
|
|
type = "os error";
|
|
break;
|
|
case 4:
|
|
type = "vss error";
|
|
break;
|
|
case 5:
|
|
type = "undefined exception";
|
|
break;
|
|
default:
|
|
type = "unknown error";
|
|
break;
|
|
}
|
|
dev_err(dev, "%s(%08X, %08X, %08X) is reported from calliope\n",
|
|
type, system_msg->param1, system_msg->param2,
|
|
system_msg->param3);
|
|
|
|
switch (system_msg->param1) {
|
|
case 1:
|
|
case 2:
|
|
abox_dbg_print_gpr_from_addr(dev, data,
|
|
abox_addr_to_kernel_addr(data,
|
|
system_msg->bundle.param_s32[0]));
|
|
abox_dbg_dump_gpr_from_addr(dev,
|
|
abox_addr_to_kernel_addr(data,
|
|
system_msg->bundle.param_s32[0]),
|
|
ABOX_DBG_DUMP_FIRMWARE, type);
|
|
abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_FIRMWARE,
|
|
type);
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
abox_debug_string_update(system_msg->param1,
|
|
abox_addr_to_kernel_addr(data, system_msg->bundle.param_s32[0]));
|
|
#endif
|
|
break;
|
|
case 4:
|
|
abox_dbg_print_gpr(dev, data);
|
|
abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_VSS, type);
|
|
abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_VSS, type);
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
abox_debug_string_update(system_msg->param1, NULL);
|
|
#endif
|
|
break;
|
|
default:
|
|
abox_dbg_print_gpr(dev, data);
|
|
abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_FIRMWARE,
|
|
type);
|
|
abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_FIRMWARE,
|
|
type);
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
abox_debug_string_update(system_msg->param1, NULL);
|
|
#endif
|
|
break;
|
|
}
|
|
abox_failsafe_report(dev);
|
|
break;
|
|
}
|
|
default:
|
|
dev_warn(dev, "Redundant system message: %d(%d, %d, %d)\n",
|
|
system_msg->msgtype, system_msg->param1,
|
|
system_msg->param2, system_msg->param3);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void abox_playback_ipc_handler(struct device *dev,
|
|
struct abox_data *data, ABOX_IPC_MSG *msg)
|
|
{
|
|
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
|
|
struct abox_platform_data *platform_data;
|
|
int id = pcmtask_msg->channel_id;
|
|
|
|
dev_dbg(dev, "msgtype=%d\n", pcmtask_msg->msgtype);
|
|
|
|
if ((id >= ARRAY_SIZE(data->pdev_rdma)) || !data->pdev_rdma[id]) {
|
|
irqreturn_t ret;
|
|
|
|
ret = abox_registered_ipc_handler(dev, IPC_PCMPLAYBACK, data,
|
|
msg, false);
|
|
if (ret != IRQ_HANDLED)
|
|
dev_err(dev, "pcm playback irq: id=%d\n", id);
|
|
return;
|
|
}
|
|
|
|
platform_data = platform_get_drvdata(data->pdev_rdma[id]);
|
|
|
|
switch (pcmtask_msg->msgtype) {
|
|
case PCM_PLTDAI_POINTER:
|
|
platform_data->pointer = pcmtask_msg->param.pointer;
|
|
snd_pcm_period_elapsed(platform_data->substream);
|
|
break;
|
|
case PCM_PLTDAI_ACK:
|
|
platform_data->ack_enabled = !!pcmtask_msg->param.trigger;
|
|
break;
|
|
default:
|
|
dev_warn(dev, "Redundant pcmtask message: %d\n",
|
|
pcmtask_msg->msgtype);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void abox_capture_ipc_handler(struct device *dev,
|
|
struct abox_data *data, ABOX_IPC_MSG *msg)
|
|
{
|
|
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
|
|
struct abox_platform_data *platform_data;
|
|
int id = pcmtask_msg->channel_id;
|
|
|
|
dev_dbg(dev, "msgtype=%d\n", pcmtask_msg->msgtype);
|
|
|
|
if ((id >= ARRAY_SIZE(data->pdev_wdma)) || (!data->pdev_wdma[id])) {
|
|
irqreturn_t ret;
|
|
|
|
ret = abox_registered_ipc_handler(dev, IPC_PCMCAPTURE, data,
|
|
msg, false);
|
|
if (ret != IRQ_HANDLED)
|
|
dev_err(dev, "pcm capture irq: id=%d\n", id);
|
|
return;
|
|
}
|
|
|
|
platform_data = platform_get_drvdata(data->pdev_wdma[id]);
|
|
|
|
switch (pcmtask_msg->msgtype) {
|
|
case PCM_PLTDAI_POINTER:
|
|
platform_data->pointer = pcmtask_msg->param.pointer;
|
|
snd_pcm_period_elapsed(platform_data->substream);
|
|
break;
|
|
case PCM_PLTDAI_ACK:
|
|
platform_data->ack_enabled = !!pcmtask_msg->param.trigger;
|
|
break;
|
|
default:
|
|
dev_warn(dev, "Redundant pcmtask message: %d\n",
|
|
pcmtask_msg->msgtype);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void abox_offload_ipc_handler(struct device *dev,
|
|
struct abox_data *data, ABOX_IPC_MSG *msg)
|
|
{
|
|
struct IPC_OFFLOADTASK_MSG *offloadtask_msg = &msg->msg.offload;
|
|
int id = offloadtask_msg->channel_id;
|
|
struct abox_platform_data *platform_data;
|
|
|
|
if (id != 5) {
|
|
dev_warn(dev, "%s: unknown channel id(%d)\n", __func__, id);
|
|
id = 5;
|
|
}
|
|
platform_data = platform_get_drvdata(data->pdev_rdma[id]);
|
|
|
|
if (platform_data->compr_data.isr_handler)
|
|
platform_data->compr_data.isr_handler(data->pdev_rdma[id]);
|
|
else
|
|
dev_warn(dev, "Redundant offload message on rdma[%d]", id);
|
|
}
|
|
|
|
static irqreturn_t abox_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct platform_device *pdev = dev_id;
|
|
struct device *dev = &pdev->dev;
|
|
struct abox_data *data = platform_get_drvdata(pdev);
|
|
ABOX_IPC_MSG msg;
|
|
|
|
if (abox_dma_irq_handler(irq, data) == IRQ_HANDLED)
|
|
goto out;
|
|
|
|
memcpy(&msg, data->sram_base + data->ipc_rx_offset, sizeof(msg));
|
|
writel(0, data->sram_base + data->ipc_rx_ack_offset);
|
|
|
|
dev_dbg(dev, "%s: irq=%d, ipcid=%d\n", __func__, irq, msg.ipcid);
|
|
|
|
switch (irq) {
|
|
case IPC_SYSTEM:
|
|
abox_system_ipc_handler(dev, data, &msg);
|
|
break;
|
|
case IPC_PCMPLAYBACK:
|
|
abox_playback_ipc_handler(dev, data, &msg);
|
|
break;
|
|
case IPC_PCMCAPTURE:
|
|
abox_capture_ipc_handler(dev, data, &msg);
|
|
break;
|
|
case IPC_OFFLOAD:
|
|
abox_offload_ipc_handler(dev, data, &msg);
|
|
break;
|
|
case IPC_ERAP:
|
|
abox_registered_ipc_handler(dev, irq, data, &msg, true);
|
|
break;
|
|
default:
|
|
abox_registered_ipc_handler(dev, irq, data, &msg, false);
|
|
break;
|
|
}
|
|
out:
|
|
abox_log_schedule_flush_all(dev);
|
|
|
|
dev_dbg(dev, "%s: exit\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int abox_cpu_pm_ipc(struct abox_data *data, bool resume)
|
|
{
|
|
const unsigned long long TIMER_RATE = 26000000;
|
|
struct device *dev = &data->pdev->dev;
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system = &msg.msg.system;
|
|
unsigned long long ktime, atime;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
ktime = sched_clock();
|
|
atime = readq_relaxed(data->sfr_base + ABOX_TIMER_CURVALUD_LSB(1));
|
|
/* clock to ns */
|
|
atime *= 500;
|
|
do_div(atime, TIMER_RATE / 2000000);
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system->msgtype = resume ? ABOX_RESUME : ABOX_SUSPEND;
|
|
system->bundle.param_u64[0] = ktime;
|
|
system->bundle.param_u64[1] = atime;
|
|
ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 1);
|
|
if (!resume) {
|
|
int i = 1000;
|
|
unsigned int val;
|
|
|
|
do {
|
|
exynos_pmu_read(ABOX_CPU_STANDBY, &val);
|
|
} while (--i && !(val & ABOX_CPU_STANDBY_WFI_MASK));
|
|
|
|
if (!(val & ABOX_CPU_STANDBY_WFI_MASK)) {
|
|
dev_warn(dev, "calliope suspend time out\n");
|
|
ret = -ETIME;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void abox_pad_retention(bool retention)
|
|
{
|
|
if (retention) {
|
|
pr_debug("%s:Do not need\n", __func__);
|
|
} else {
|
|
#ifndef EMULATOR
|
|
exynos_pmu_update(PAD_RETENTION_ABOX_OPTION,
|
|
0x10000000, 0x10000000);
|
|
#else
|
|
update_mask_value(pmu_alive + PAD_RETENTION_ABOX_OPTION,
|
|
0x10000000, 0x10000000);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void abox_cpu_power(bool on)
|
|
{
|
|
pr_info("%s(%d)\n", __func__, on);
|
|
|
|
#ifndef EMULATOR
|
|
exynos_pmu_update(ABOX_CPU_CONFIGURATION, ABOX_CPU_LOCAL_PWR_CFG,
|
|
on ? ABOX_CPU_LOCAL_PWR_CFG : 0);
|
|
#else
|
|
update_mask_value(pmu_alive + ABOX_CPU_CONFIGURATION,
|
|
ABOX_CPU_LOCAL_PWR_CFG,
|
|
on ? ABOX_CPU_LOCAL_PWR_CFG : 0);
|
|
#endif
|
|
}
|
|
|
|
static int abox_cpu_enable(bool enable)
|
|
{
|
|
unsigned int mask = ABOX_CPU_OPTION_ENABLE_CPU_MASK;
|
|
unsigned int val = (enable ? mask : 0);
|
|
unsigned int status = 0;
|
|
unsigned long after;
|
|
|
|
|
|
pr_info("%s(%d)\n", __func__, enable);
|
|
|
|
#ifndef EMULATOR
|
|
exynos_pmu_update(ABOX_CPU_OPTION, mask, val);
|
|
#else
|
|
update_mask_value(pmu_alive + ABOX_CPU_OPTION, mask, val);
|
|
#endif
|
|
if (enable) {
|
|
after = jiffies + LIMIT_IN_JIFFIES;
|
|
do {
|
|
#ifndef EMULATOR
|
|
exynos_pmu_read(ABOX_CPU_STATUS, &status);
|
|
#else
|
|
status = readl(pmu_alive + ABOX_CPU_STATUS);
|
|
#endif
|
|
} while (((status & ABOX_CPU_STATUS_STATUS_MASK)
|
|
!= ABOX_CPU_STATUS_STATUS_MASK)
|
|
&& time_is_after_eq_jiffies(after));
|
|
if (time_is_before_jiffies(after)) {
|
|
pr_err("abox cpu enable timeout\n");
|
|
return -ETIME;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void abox_save_register(struct abox_data *data)
|
|
{
|
|
regcache_cache_only(data->regmap, true);
|
|
regcache_mark_dirty(data->regmap);
|
|
}
|
|
|
|
static void abox_restore_register(struct abox_data *data)
|
|
{
|
|
regcache_cache_only(data->regmap, false);
|
|
regcache_sync(data->regmap);
|
|
}
|
|
|
|
static void abox_reload_extra_firmware(struct abox_data *data, const char *name)
|
|
{
|
|
struct platform_device *pdev = data->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
struct abox_extra_firmware *ext_fw;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%s)\n", __func__, name);
|
|
|
|
for (ext_fw = data->firmware_extra; ext_fw - data->firmware_extra <
|
|
ARRAY_SIZE(data->firmware_extra); ext_fw++) {
|
|
if (!ext_fw->name || strcmp(ext_fw->name, name))
|
|
continue;
|
|
|
|
release_firmware(ext_fw->firmware);
|
|
ret = request_firmware(&ext_fw->firmware, ext_fw->name, dev);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: %s request failed\n", __func__,
|
|
ext_fw->name);
|
|
break;
|
|
}
|
|
dev_info(dev, "%s is reloaded\n", name);
|
|
}
|
|
}
|
|
|
|
static void abox_request_extra_firmware(struct abox_data *data)
|
|
{
|
|
struct platform_device *pdev = data->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct device_node *child_np;
|
|
struct abox_extra_firmware *ext_fw;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
ext_fw = data->firmware_extra;
|
|
for_each_child_of_node(np, child_np) {
|
|
const char *status;
|
|
|
|
status = of_get_property(child_np, "status", NULL);
|
|
if (status && strcmp("okay", status) && strcmp("ok", status))
|
|
continue;
|
|
|
|
ret = of_property_read_string(child_np, "samsung,name",
|
|
&ext_fw->name);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = of_property_read_u32(child_np, "samsung,area",
|
|
&ext_fw->area);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = of_property_read_u32(child_np, "samsung,offset",
|
|
&ext_fw->offset);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
dev_dbg(dev, "%s: name=%s, area=%u, offset=%u\n", __func__,
|
|
ext_fw->name, ext_fw->area, ext_fw->offset);
|
|
|
|
if (!ext_fw->firmware) {
|
|
dev_dbg(dev, "%s: request %s\n", __func__,
|
|
ext_fw->name);
|
|
ret = request_firmware(&ext_fw->firmware,
|
|
ext_fw->name, dev);
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: %s request failed\n",
|
|
__func__, ext_fw->name);
|
|
}
|
|
ext_fw++;
|
|
}
|
|
|
|
}
|
|
|
|
static void abox_download_extra_firmware(struct abox_data *data)
|
|
{
|
|
struct device *dev = &data->pdev->dev;
|
|
struct abox_extra_firmware *ext_fw;
|
|
void __iomem *base;
|
|
size_t size;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (ext_fw = data->firmware_extra; ext_fw - data->firmware_extra <
|
|
ARRAY_SIZE(data->firmware_extra); ext_fw++) {
|
|
if (!ext_fw->firmware)
|
|
continue;
|
|
|
|
switch (ext_fw->area) {
|
|
case 0:
|
|
base = data->sram_base;
|
|
size = data->sram_size;
|
|
break;
|
|
case 1:
|
|
base = data->dram_base;
|
|
size = DRAM_FIRMWARE_SIZE;
|
|
break;
|
|
case 2:
|
|
if (IS_ENABLED(CONFIG_SHM_IPC)) {
|
|
base = phys_to_virt(shm_get_vss_base());
|
|
size = shm_get_vss_size();
|
|
}
|
|
break;
|
|
default:
|
|
dev_err(dev, "%s: area is invalid name=%s, area=%u, offset=%u\n",
|
|
__func__, ext_fw->name, ext_fw->area,
|
|
ext_fw->offset);
|
|
continue;
|
|
}
|
|
|
|
if (ext_fw->offset + ext_fw->firmware->size > size) {
|
|
dev_err(dev, "%s: firmware is too large name=%s, area=%u, offset=%u\n",
|
|
__func__, ext_fw->name, ext_fw->area,
|
|
ext_fw->offset);
|
|
continue;
|
|
}
|
|
|
|
memcpy(base + ext_fw->offset, ext_fw->firmware->data,
|
|
ext_fw->firmware->size);
|
|
dev_info(dev, "%s is downloaded at area %u offset %u\n",
|
|
ext_fw->name, ext_fw->area, ext_fw->offset);
|
|
}
|
|
}
|
|
|
|
static int abox_request_firmware(struct device *dev,
|
|
const struct firmware **fw, const char *name)
|
|
{
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
release_firmware(*fw);
|
|
ret = request_firmware(fw, name, dev);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: %s request failed\n", __func__, name);
|
|
} else {
|
|
dev_info(dev, "%s is loaded\n", name);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void abox_complete_sram_firmware_request(const struct firmware *fw,
|
|
void *context)
|
|
{
|
|
struct platform_device *pdev = context;
|
|
struct device *dev = &pdev->dev;
|
|
struct abox_data *data = platform_get_drvdata(pdev);
|
|
|
|
if (!fw) {
|
|
dev_err(dev, "Failed to request firmware\n");
|
|
return;
|
|
}
|
|
|
|
if (data->firmware_sram)
|
|
release_firmware(data->firmware_sram);
|
|
|
|
data->firmware_sram = fw;
|
|
|
|
dev_info(dev, "SRAM firmware loaded\n");
|
|
|
|
abox_request_firmware(dev, &data->firmware_dram, "calliope_dram.bin");
|
|
abox_request_extra_firmware(data);
|
|
|
|
if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND))
|
|
if (pm_runtime_active(dev))
|
|
abox_enable(dev);
|
|
}
|
|
|
|
static int abox_download_firmware(struct platform_device *pdev)
|
|
{
|
|
struct abox_data *data = platform_get_drvdata(pdev);
|
|
struct device *dev = &pdev->dev;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
if (unlikely(!data->firmware_sram)) {
|
|
request_firmware_nowait(THIS_MODULE,
|
|
FW_ACTION_HOTPLUG,
|
|
"calliope_sram.bin",
|
|
dev,
|
|
GFP_KERNEL,
|
|
pdev,
|
|
abox_complete_sram_firmware_request);
|
|
dev_warn(dev, "SRAM firmware downloading is deferred\n");
|
|
return -EAGAIN;
|
|
}
|
|
memcpy_toio(data->sram_base, data->firmware_sram->data,
|
|
data->firmware_sram->size);
|
|
memset_io(data->sram_base + data->firmware_sram->size, 0,
|
|
data->sram_size - data->firmware_sram->size);
|
|
|
|
if (unlikely(!data->firmware_dram)) {
|
|
dev_warn(dev, "DRAM firmware downloading is defferred\n");
|
|
return -EAGAIN;
|
|
}
|
|
memcpy(data->dram_base, data->firmware_dram->data,
|
|
data->firmware_dram->size);
|
|
memset(data->dram_base + data->firmware_dram->size, 0,
|
|
DRAM_FIRMWARE_SIZE - data->firmware_dram->size);
|
|
|
|
if ((data->bootargs_offset != 0) && (data->bootargs != NULL)) {
|
|
dev_info(dev, "bootargs[%p][0x%x][%s]\n",
|
|
data->sram_base,
|
|
data->bootargs_offset, data->bootargs);
|
|
|
|
memcpy_toio(data->sram_base + data->bootargs_offset,
|
|
data->bootargs, SZ_512);
|
|
|
|
} else {
|
|
dev_info(dev, "bootargs is NULL\n");
|
|
}
|
|
|
|
abox_download_extra_firmware(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void abox_cfg_gpio(struct device *dev, const char *name)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
struct pinctrl_state *pin_state;
|
|
int ret;
|
|
|
|
dev_info(dev, "%s(%s)\n", __func__, name);
|
|
|
|
if (!data->pinctrl)
|
|
return;
|
|
|
|
pin_state = pinctrl_lookup_state(data->pinctrl, name);
|
|
if (IS_ERR(pin_state)) {
|
|
dev_err(dev, "Couldn't find pinctrl %s\n", name);
|
|
} else {
|
|
ret = pinctrl_select_state(data->pinctrl, pin_state);
|
|
if (ret < 0)
|
|
dev_err(dev, "Unable to configure pinctrl %s\n", name);
|
|
}
|
|
}
|
|
|
|
#undef MANUAL_SECURITY_CHANGE
|
|
#ifdef MANUAL_SECURITY_CHANGE
|
|
static void work_temp_function(struct work_struct *work)
|
|
{
|
|
exynos_smc(0x82000701, 0, 0, 0);
|
|
pr_err("%s: ABOX_CA7 security changed!!!\n", __func__);
|
|
}
|
|
static DECLARE_DELAYED_WORK(work_temp, work_temp_function);
|
|
#endif
|
|
|
|
static void __abox_control_l2c(struct abox_data *data, bool enable)
|
|
{
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
struct device *dev = &data->pdev->dev;
|
|
|
|
if (data->l2c_enabled == enable)
|
|
return;
|
|
|
|
dev_info(dev, "%s(%d)\n", __func__, enable);
|
|
|
|
data->l2c_controlled = false;
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_START_L2C_CONTROL;
|
|
system_msg->param1 = enable ? 1 : 0;
|
|
|
|
if (enable) {
|
|
vts_acquire_sram(data->pdev_vts, 0);
|
|
|
|
abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
|
|
wait_event_timeout(data->ipc_wait_queue,
|
|
data->l2c_controlled, LIMIT_IN_JIFFIES);
|
|
if (!data->l2c_controlled)
|
|
dev_err(dev, "l2c enable failed\n");
|
|
} else {
|
|
abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
|
|
wait_event_timeout(data->ipc_wait_queue,
|
|
data->l2c_controlled, LIMIT_IN_JIFFIES);
|
|
if (!data->l2c_controlled)
|
|
dev_err(dev, "l2c disable failed\n");
|
|
|
|
vts_release_sram(data->pdev_vts, 0);
|
|
}
|
|
|
|
data->l2c_enabled = enable;
|
|
}
|
|
|
|
static void abox_l2c_work_func(struct work_struct *work)
|
|
{
|
|
struct abox_data *data = container_of(work, struct abox_data, l2c_work);
|
|
struct platform_device *pdev = data->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
size_t length = ARRAY_SIZE(data->l2c_requests);
|
|
struct abox_l2c_request *request;
|
|
bool enable = false;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
for (request = data->l2c_requests;
|
|
request - data->l2c_requests < length
|
|
&& request->id;
|
|
request++) {
|
|
if (request->on) {
|
|
enable = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
__abox_control_l2c(data, enable);
|
|
}
|
|
|
|
int abox_request_l2c(struct device *dev, struct abox_data *data,
|
|
void *id, bool on)
|
|
{
|
|
struct abox_l2c_request *request;
|
|
size_t length = ARRAY_SIZE(data->l2c_requests);
|
|
|
|
if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
|
|
return 0;
|
|
|
|
dev_info(dev, "%s(%#lx, %d)\n", __func__, (unsigned long)id, on);
|
|
|
|
for (request = data->l2c_requests;
|
|
request - data->l2c_requests < length
|
|
&& request->id && request->id != id;
|
|
request++) {
|
|
}
|
|
|
|
request->on = on;
|
|
wmb(); /* on is read after id in reading function */
|
|
request->id = id;
|
|
|
|
if (request - data->l2c_requests >= ARRAY_SIZE(data->l2c_requests)) {
|
|
dev_err(dev, "%s: out of index. id=%#lx, on=%d\n",
|
|
__func__, (unsigned long)id, on);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
schedule_work(&data->l2c_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int abox_request_l2c_sync(struct device *dev, struct abox_data *data,
|
|
void *id, bool on)
|
|
{
|
|
if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
|
|
return 0;
|
|
|
|
abox_request_l2c(dev, data, id, on);
|
|
flush_work(&data->l2c_work);
|
|
return 0;
|
|
}
|
|
|
|
static void abox_clear_l2c_requests(struct device *dev, struct abox_data *data)
|
|
{
|
|
struct abox_l2c_request *req;
|
|
size_t len = ARRAY_SIZE(data->l2c_requests);
|
|
|
|
if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
|
|
return;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
for (req = data->l2c_requests; req - data->l2c_requests < len &&
|
|
req->id; req++) {
|
|
req->on = false;
|
|
}
|
|
|
|
__abox_control_l2c(data, false);
|
|
}
|
|
|
|
static bool abox_is_timer_set(struct abox_data *data)
|
|
{
|
|
unsigned int val;
|
|
int ret;
|
|
|
|
ret = regmap_read(data->regmap, ABOX_TIMER_CTRL1(0), &val);
|
|
if (ret < 0)
|
|
val = 0;
|
|
|
|
return !!val;
|
|
}
|
|
|
|
static void abox_start_timer(struct abox_data *data)
|
|
{
|
|
struct regmap *regmap = data->regmap;
|
|
|
|
regmap_write(regmap, ABOX_TIMER_CTRL0(0), 1 << ABOX_TIMER_START_L);
|
|
}
|
|
|
|
static int abox_enable(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
unsigned int i, value;
|
|
bool has_reset;
|
|
int ret = 0;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
abox_gic_enable_irq(data->dev_gic);
|
|
|
|
abox_request_cpu_gear(dev, data, DEFAULT_CPU_GEAR_ID, ABOX_CPU_GEAR_MAX);
|
|
ret = clk_set_rate(data->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
|
|
if (ret < 0)
|
|
dev_warn(dev, "setting audio pll clock to 1.2Ghz is failed: %d\n", ret);
|
|
|
|
if (is_secure_gic()) {
|
|
exynos_pmu_write(ABOX_MAGIC, 0);
|
|
ret = exynos_smc(0x82000501, 0, 0, 0);
|
|
dev_dbg(dev, "%s: smc ret=%d\n", __func__, ret);
|
|
|
|
for (i = 1000; i; i--) {
|
|
exynos_pmu_read(ABOX_MAGIC, &value);
|
|
if (value == ABOX_MAGIC_VALUE)
|
|
break;
|
|
}
|
|
if (value != ABOX_MAGIC_VALUE)
|
|
dev_warn(dev, "%s: abox magic timeout\n", __func__);
|
|
abox_cpu_enable(false);
|
|
abox_cpu_power(false);
|
|
}
|
|
|
|
if (abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM)) {
|
|
writel(0x1, data->sysreg_base + ABOX_SYSREG_MISC_CON);
|
|
writel(0x1, data->sysreg_base + ABOX_SYSREG_L2_CACHE_CON);
|
|
}
|
|
|
|
ret = clk_enable(data->clk_cpu);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to enable cpu clock: %d\n", ret);
|
|
goto error;
|
|
}
|
|
|
|
ret = clk_set_rate(data->clk_audif, AUDIF_RATE_HZ);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to set audif clock: %d\n", ret);
|
|
goto error;
|
|
}
|
|
dev_info(dev, "audif clock: %lu\n", clk_get_rate(data->clk_audif));
|
|
|
|
ret = clk_enable(data->clk_audif);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to enable audif clock: %d\n", ret);
|
|
goto error;
|
|
}
|
|
|
|
abox_cfg_gpio(dev, "default");
|
|
|
|
abox_restore_register(data);
|
|
has_reset = !abox_is_timer_set(data);
|
|
if (!has_reset) {
|
|
dev_info(dev, "wakeup from WFI\n");
|
|
abox_start_timer(data);
|
|
} else {
|
|
abox_gic_init_gic(data->dev_gic);
|
|
|
|
ret = abox_download_firmware(pdev);
|
|
if (ret < 0) {
|
|
if (ret != -EAGAIN)
|
|
dev_err(dev, "Failed to download firmware\n");
|
|
else
|
|
ret = 0;
|
|
|
|
abox_request_cpu_gear(dev, data, DEFAULT_CPU_GEAR_ID,
|
|
ABOX_CPU_GEAR_MIN);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
abox_request_dram_on(pdev, dev, true);
|
|
if (has_reset) {
|
|
abox_cpu_power(true);
|
|
abox_cpu_enable(true);
|
|
}
|
|
data->calliope_state = CALLIOPE_ENABLING;
|
|
|
|
abox_pad_retention(false);
|
|
#ifdef MANUAL_SECURITY_CHANGE
|
|
schedule_delayed_work(&work_temp, msecs_to_jiffies(3000));
|
|
#endif
|
|
|
|
data->enabled = true;
|
|
|
|
if (has_reset)
|
|
pm_wakeup_event(dev, BOOT_DONE_TIMEOUT_MS);
|
|
else
|
|
abox_boot_done(dev, data->calliope_version);
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
static int abox_disable(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
enum calliope_state state = data->calliope_state;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
/* AUD_PLL must be normal during suspend */
|
|
clk_set_rate(data->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
|
|
|
|
data->calliope_state = CALLIOPE_DISABLING;
|
|
abox_cache_components(dev, data);
|
|
abox_clear_l2c_requests(dev, data);
|
|
flush_work(&data->boot_done_work);
|
|
flush_work(&data->l2c_work);
|
|
if (state != CALLIOPE_DISABLED)
|
|
abox_cpu_pm_ipc(data, false);
|
|
data->calliope_state = CALLIOPE_DISABLED;
|
|
abox_log_drain_all(dev);
|
|
abox_request_dram_on(pdev, dev, false);
|
|
abox_save_register(data);
|
|
abox_cfg_gpio(dev, "idle");
|
|
abox_pad_retention(true);
|
|
data->enabled = false;
|
|
clk_disable(data->clk_cpu);
|
|
abox_gic_disable_irq(data->dev_gic);
|
|
abox_failsafe_report_reset(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void abox_poweroff(void)
|
|
{
|
|
struct platform_device *pdev = p_abox_data->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
|
|
if (data->calliope_state == CALLIOPE_DISABLED) {
|
|
dev_dbg(dev, "already disabled\n");
|
|
return;
|
|
}
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
abox_disable(dev);
|
|
|
|
exynos_sysmmu_control(dev, false);
|
|
}
|
|
|
|
static int abox_runtime_suspend(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
p_abox_data->enabled = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_runtime_resume(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
exynos_sysmmu_control(dev, true);
|
|
|
|
return abox_enable(dev);
|
|
}
|
|
|
|
static int abox_suspend(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
/* nothing to do */
|
|
return 0;
|
|
}
|
|
|
|
static int abox_resume(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
/* nothing to do */
|
|
return 0;
|
|
}
|
|
|
|
static int abox_qos_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
struct abox_data *data = container_of(nb, struct abox_data, qos_nb);
|
|
struct device *dev = &data->pdev->dev;
|
|
long value = (long)action;
|
|
long qos_class = (long)nb_data;
|
|
unsigned long aclk = clk_get_rate(data->clk_bus);
|
|
unsigned int sifs_cnt0, sifs_cnt1, cnt_val, rate, pwidth, channels;
|
|
unsigned long sifs0_cnt, sifs1_cnt, sifs2_cnt;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%ldkHz, %ld)\n", __func__, value, qos_class);
|
|
|
|
ret = regmap_read(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0, &sifs_cnt0);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: SPUS_CTRL_SIFS_CNT0 read fail: %d\n",
|
|
__func__, ret);
|
|
goto out;
|
|
}
|
|
ret = regmap_read(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT1, &sifs_cnt1);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: SPUS_CTRL_SIFS_CNT1 read fail: %d\n",
|
|
__func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
sifs0_cnt = (sifs_cnt0 & ABOX_SIFS0_CNT_VAL_MASK) >>
|
|
ABOX_SIFS0_CNT_VAL_L;
|
|
sifs1_cnt = (sifs_cnt0 & ABOX_SIFS1_CNT_VAL_MASK) >>
|
|
ABOX_SIFS1_CNT_VAL_L;
|
|
sifs2_cnt = (sifs_cnt1 & ABOX_SIFS2_CNT_VAL_MASK) >>
|
|
ABOX_SIFS2_CNT_VAL_L;
|
|
|
|
if (sifs0_cnt) {
|
|
rate = abox_get_sif_rate(data, SET_MIXER_SAMPLE_RATE);
|
|
pwidth = abox_get_sif_physical_width(data, SET_MIXER_FORMAT);
|
|
channels = abox_get_sif_channels(data, SET_MIXER_FORMAT);
|
|
cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
|
|
dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
|
|
cnt_val);
|
|
ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS0_CNT_VAL_MASK,
|
|
(unsigned int)cnt_val << ABOX_SIFS0_CNT_VAL_L);
|
|
if (ret < 0)
|
|
dev_err(dev, "regmap update failed: %d\n", ret);
|
|
}
|
|
if (sifs1_cnt) {
|
|
rate = abox_get_sif_rate(data, SET_OUT1_SAMPLE_RATE);
|
|
pwidth = abox_get_sif_physical_width(data, SET_OUT1_FORMAT);
|
|
channels = abox_get_sif_channels(data, SET_OUT1_FORMAT);
|
|
cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
|
|
dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
|
|
cnt_val);
|
|
ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
|
|
ABOX_SIFS1_CNT_VAL_MASK,
|
|
(unsigned int)cnt_val << ABOX_SIFS1_CNT_VAL_L);
|
|
if (ret < 0)
|
|
dev_err(dev, "regmap update failed: %d\n", ret);
|
|
}
|
|
if (sifs2_cnt) {
|
|
rate = abox_get_sif_rate(data, SET_OUT2_SAMPLE_RATE);
|
|
pwidth = abox_get_sif_physical_width(data, SET_OUT2_FORMAT);
|
|
channels = abox_get_sif_channels(data, SET_OUT2_FORMAT);
|
|
cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
|
|
dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
|
|
cnt_val);
|
|
ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
|
|
ABOX_SIFS2_CNT_VAL_MASK,
|
|
(unsigned int)cnt_val << ABOX_SIFS2_CNT_VAL_L);
|
|
if (ret < 0)
|
|
dev_err(dev, "regmap update failed: %d\n", ret);
|
|
}
|
|
out:
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static int abox_print_power_usage(struct device *dev, void *data)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
if (pm_runtime_enabled(dev) && pm_runtime_active(dev)) {
|
|
dev_info(dev, "usage_count:%d\n",
|
|
atomic_read(&dev->power.usage_count));
|
|
device_for_each_child(dev, data, abox_print_power_usage);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abox_pm_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
struct abox_data *data = container_of(nb, struct abox_data, pm_nb);
|
|
struct device *dev = &data->pdev->dev;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s(%lu)\n", __func__, action);
|
|
|
|
switch (action) {
|
|
case PM_SUSPEND_PREPARE:
|
|
if (data->audio_mode != MODE_IN_CALL) {
|
|
enum calliope_state state;
|
|
|
|
pm_runtime_barrier(dev);
|
|
state = data->calliope_state;
|
|
if (state == CALLIOPE_ENABLING) {
|
|
dev_info(dev, "calliope state: %d\n", state);
|
|
return NOTIFY_BAD;
|
|
}
|
|
/* clear cpu gears to abox power off */
|
|
abox_clear_cpu_gear_requests(dev, data);
|
|
abox_cpu_gear_barrier(data);
|
|
flush_workqueue(data->ipc_workqueue);
|
|
if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND)) {
|
|
ret = pm_runtime_put_sync(dev);
|
|
if (ret < 0) {
|
|
pm_runtime_get(dev);
|
|
dev_info(dev, "runtime put sync: %d\n", ret);
|
|
abox_print_power_usage(dev, NULL);
|
|
return NOTIFY_BAD;
|
|
} else if (ret == 0 && atomic_read(&dev->power.usage_count) > 0) {
|
|
dev_info(dev, "runtime put sync: %d uc(%d)\n",
|
|
ret, atomic_read(&dev->power.usage_count));
|
|
pm_runtime_get(dev);
|
|
abox_print_power_usage(dev, NULL);
|
|
return NOTIFY_BAD;
|
|
}
|
|
ret = pm_runtime_suspend(dev);
|
|
if (ret < 0) {
|
|
dev_info(dev, "runtime suspend: %d\n", ret);
|
|
abox_print_power_usage(dev, NULL);
|
|
return NOTIFY_BAD;
|
|
}
|
|
atomic_set(&data->suspend_state, 1);
|
|
dev_info(dev, "(%d)s suspend_state: %d\n", __LINE__,
|
|
atomic_read(&data->suspend_state));
|
|
} else {
|
|
ret = pm_runtime_suspend(dev);
|
|
if (ret < 0) {
|
|
dev_info(dev, "runtime suspend: %d\n",
|
|
ret);
|
|
return NOTIFY_BAD;
|
|
}
|
|
}
|
|
} else {
|
|
dev_info(dev, "abox is not clearable()\n");
|
|
}
|
|
break;
|
|
case PM_POST_SUSPEND:
|
|
if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND)) {
|
|
dev_info(dev, "(%d)r suspend_state: %d\n", __LINE__,
|
|
atomic_read(&data->suspend_state));
|
|
if (atomic_read(&data->suspend_state) == 1) {
|
|
pm_runtime_get_sync(&data->pdev->dev);
|
|
atomic_set(&data->suspend_state, 0);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
/* Nothing to do */
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static int abox_modem_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
struct abox_data *data = container_of(nb, struct abox_data, modem_nb);
|
|
struct device *dev = &data->pdev->dev;
|
|
ABOX_IPC_MSG msg;
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
|
|
dev_info(&data->pdev->dev, "%s(%lu)\n", __func__, action);
|
|
|
|
switch (action) {
|
|
case MODEM_EVENT_ONLINE:
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_START_VSS;
|
|
abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
|
|
break;
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
case MODEM_EVENT_RESET:
|
|
case MODEM_EVENT_EXIT:
|
|
case MODEM_EVENT_WATCHDOG:
|
|
abox_debug_string_update(TYPE_ABOX_VSSERROR, NULL);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
#ifdef CONFIG_EXYNOS_ITMON
|
|
static int abox_itmon_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
struct abox_data *data = container_of(nb, struct abox_data, itmon_nb);
|
|
struct device *dev = &data->pdev->dev;
|
|
struct itmon_notifier *itmon_data = nb_data;
|
|
|
|
if (itmon_data && itmon_data->dest && (strncmp("ABOX", itmon_data->dest,
|
|
sizeof("ABOX") - 1) == 0)) {
|
|
dev_info(dev, "%s(%lu)\n", __func__, action);
|
|
data->enabled = false;
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
#endif
|
|
|
|
static ssize_t calliope_version_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
unsigned int version = be32_to_cpu(data->calliope_version);
|
|
|
|
memcpy(buf, &version, sizeof(version));
|
|
buf[4] = '\n';
|
|
buf[5] = '\0';
|
|
|
|
return 6;
|
|
}
|
|
|
|
static ssize_t calliope_debug_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
ABOX_IPC_MSG msg = {0,};
|
|
struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
msg.ipcid = IPC_SYSTEM;
|
|
system_msg->msgtype = ABOX_REQUEST_DEBUG;
|
|
ret = sscanf(buf, "%10d,%10d,%10d,%739s", &system_msg->param1,
|
|
&system_msg->param2, &system_msg->param3,
|
|
system_msg->bundle.param_bundle);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t calliope_cmd_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
static const char cmd_reload_ext_bin[] = "RELOAD EXT BIN";
|
|
static const char cmd_failsafe[] = "FAILSAFE";
|
|
static const char cmd_cpu_gear[] = "CPU GEAR";
|
|
struct abox_data *data = dev_get_drvdata(dev);
|
|
char name[80];
|
|
|
|
dev_dbg(dev, "%s(%s)\n", __func__, buf);
|
|
if (!strncmp(cmd_reload_ext_bin, buf, sizeof(cmd_reload_ext_bin) - 1)) {
|
|
dev_dbg(dev, "reload ext bin\n");
|
|
if (sscanf(buf, "RELOAD EXT BIN:%63s", name) == 1)
|
|
abox_reload_extra_firmware(data, name);
|
|
} else if (!strncmp(cmd_failsafe, buf, sizeof(cmd_failsafe) - 1)) {
|
|
dev_dbg(dev, "failsafe\n");
|
|
abox_failsafe_report(dev);
|
|
} else if (!strncmp(cmd_cpu_gear, buf, sizeof(cmd_cpu_gear) - 1)) {
|
|
unsigned int gear;
|
|
int ret;
|
|
|
|
dev_info(dev, "set clk\n");
|
|
ret = kstrtouint(buf + sizeof(cmd_cpu_gear), 10, &gear);
|
|
if (!ret) {
|
|
dev_info(dev, "gear = %u\n", gear);
|
|
pm_runtime_get_sync(dev);
|
|
abox_request_cpu_gear(dev, data, TEST_CPU_GEAR_ID,
|
|
gear);
|
|
dev_info(dev, "bus clk = %lu\n",
|
|
clk_get_rate(data->clk_bus));
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(calliope_version);
|
|
static DEVICE_ATTR_WO(calliope_debug);
|
|
static DEVICE_ATTR_WO(calliope_cmd);
|
|
|
|
static int samsung_abox_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct device_node *np_tmp;
|
|
struct platform_device *pdev_tmp;
|
|
struct abox_data *data;
|
|
int ret, i;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, data);
|
|
data->pdev = pdev;
|
|
p_abox_data = data;
|
|
|
|
atomic_set(&data->suspend_state, 0);
|
|
|
|
abox_probe_quirks(data, np);
|
|
init_waitqueue_head(&data->ipc_wait_queue);
|
|
spin_lock_init(&data->ipc_queue_lock);
|
|
mutex_init(&data->iommu_lock);
|
|
device_init_wakeup(dev, true);
|
|
data->cpu_gear = ABOX_CPU_GEAR_MIN;
|
|
data->cpu_gear_min = 3; /* default value from kangchen */
|
|
for (i = 0; i < ARRAY_SIZE(data->sif_rate); i++) {
|
|
data->sif_rate_min[i] = data->sif_rate[i] = 48000;
|
|
data->sif_format_min[i] = data->sif_format[i] =
|
|
SNDRV_PCM_FORMAT_S16;
|
|
data->sif_channels_min[i] = data->sif_channels[i] = 2;
|
|
}
|
|
INIT_WORK(&data->ipc_work, abox_process_ipc);
|
|
INIT_WORK(&data->change_cpu_gear_work, abox_change_cpu_gear_work_func);
|
|
INIT_WORK(&data->change_int_freq_work, abox_change_int_freq_work_func);
|
|
INIT_WORK(&data->change_mif_freq_work, abox_change_mif_freq_work_func);
|
|
INIT_WORK(&data->change_lit_freq_work, abox_change_lit_freq_work_func);
|
|
INIT_WORK(&data->change_big_freq_work, abox_change_big_freq_work_func);
|
|
INIT_WORK(&data->change_hmp_boost_work,
|
|
abox_change_hmp_boost_work_func);
|
|
INIT_WORK(&data->register_component_work,
|
|
abox_register_component_work_func);
|
|
INIT_WORK(&data->boot_done_work, abox_boot_done_work_func);
|
|
INIT_WORK(&data->l2c_work, abox_l2c_work_func);
|
|
INIT_DELAYED_WORK(&data->tickle_work, abox_tickle_work_func);
|
|
INIT_LIST_HEAD(&data->irq_actions);
|
|
INIT_LIST_HEAD_RCU(&data->iommu_maps);
|
|
|
|
data->gear_workqueue = alloc_ordered_workqueue("abox_gear",
|
|
WQ_FREEZABLE | WQ_MEM_RECLAIM);
|
|
if (!data->gear_workqueue) {
|
|
dev_err(dev, "Couldn't create workqueue %s\n", "abox_gear");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
data->ipc_workqueue = alloc_ordered_workqueue("abox_ipc",
|
|
WQ_MEM_RECLAIM);
|
|
if (!data->ipc_workqueue) {
|
|
dev_err(dev, "Couldn't create workqueue %s\n", "abox_ipc");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
data->pinctrl = devm_pinctrl_get(dev);
|
|
if (IS_ERR(data->pinctrl)) {
|
|
dev_err(dev, "Couldn't get pins (%li)\n",
|
|
PTR_ERR(data->pinctrl));
|
|
data->pinctrl = NULL;
|
|
}
|
|
|
|
data->sfr_base = devm_request_and_map_byname(pdev, "sfr",
|
|
NULL, NULL);
|
|
if (IS_ERR(data->sfr_base))
|
|
return PTR_ERR(data->sfr_base);
|
|
|
|
data->sysreg_base = devm_request_and_map_byname(pdev, "sysreg",
|
|
NULL, NULL);
|
|
if (IS_ERR(data->sysreg_base))
|
|
return PTR_ERR(data->sysreg_base);
|
|
|
|
data->sram_base = devm_request_and_map_byname(pdev, "sram",
|
|
&data->sram_base_phys, &data->sram_size);
|
|
if (IS_ERR(data->sram_base))
|
|
return PTR_ERR(data->sram_base);
|
|
|
|
data->iommu_domain = get_domain_from_dev(dev);
|
|
if (IS_ERR(data->iommu_domain)) {
|
|
dev_err(dev, "Unable to get iommu domain\n");
|
|
return PTR_ERR(data->iommu_domain);
|
|
}
|
|
|
|
ret = iommu_attach_device(data->iommu_domain, dev);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Unable to attach device to iommu (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
data->dram_base = dmam_alloc_coherent(dev, DRAM_FIRMWARE_SIZE,
|
|
&data->dram_base_phys, GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(data->dram_base)) {
|
|
dev_err(dev, "Failed to allocate coherent memory: %ld\n",
|
|
PTR_ERR(data->dram_base));
|
|
return PTR_ERR(data->dram_base);
|
|
}
|
|
dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
|
|
"dram firmware", &data->dram_base_phys, data->dram_base,
|
|
DRAM_FIRMWARE_SIZE);
|
|
abox_iommu_map(dev, IOVA_DRAM_FIRMWARE, data->dram_base_phys,
|
|
DRAM_FIRMWARE_SIZE, data->dram_base);
|
|
|
|
data->priv_base = dmam_alloc_coherent(dev, PRIVATE_SIZE,
|
|
&data->priv_base_phys, GFP_KERNEL);
|
|
if (!data->priv_base) {
|
|
dev_err(dev, "%s: no memory\n", "private");
|
|
return -ENOMEM;
|
|
}
|
|
dev_info(dev, "%s: %pa(%#x) => %p\n", "private",
|
|
&data->priv_base_phys, PRIVATE_SIZE, data->priv_base);
|
|
iommu_map(data->iommu_domain, IOVA_PRIVATE, data->priv_base_phys,
|
|
PRIVATE_SIZE, 0);
|
|
|
|
if (IS_ENABLED(CONFIG_SHM_IPC)) {
|
|
phys_addr_t paddr;
|
|
|
|
paddr = shm_get_vss_base();
|
|
dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
|
|
"vss firmware", &paddr, phys_to_virt(paddr),
|
|
shm_get_vss_size());
|
|
abox_iommu_map(dev, IOVA_VSS_FIRMWARE, paddr, shm_get_vss_size(),
|
|
shm_get_vss_region());
|
|
|
|
paddr = shm_get_vparam_base();
|
|
dev_info(dev, "%s(%#x) alloc\n", "vss parameter",
|
|
shm_get_vparam_size());
|
|
abox_iommu_map(dev, IOVA_VSS_PARAMETER, paddr, shm_get_vparam_size(), 0);
|
|
}
|
|
|
|
abox_iommu_map(dev, 0x10000000, 0x10000000, PAGE_SIZE, 0);
|
|
iovmm_set_fault_handler(&pdev->dev, abox_iommu_fault_handler, data);
|
|
|
|
data->clk_pll = devm_clk_get_and_prepare(pdev, "pll");
|
|
if (IS_ERR(data->clk_pll))
|
|
return PTR_ERR(data->clk_pll);
|
|
|
|
data->clk_audif = devm_clk_get_and_prepare(pdev, "audif");
|
|
if (IS_ERR(data->clk_audif))
|
|
return PTR_ERR(data->clk_audif);
|
|
|
|
data->clk_cpu = devm_clk_get_and_prepare(pdev, "cpu");
|
|
if (IS_ERR(data->clk_cpu))
|
|
return PTR_ERR(data->clk_cpu);
|
|
|
|
data->clk_bus = devm_clk_get_and_prepare(pdev, "bus");
|
|
if (IS_ERR(data->clk_bus))
|
|
return PTR_ERR(data->clk_bus);
|
|
|
|
ret = of_property_read_u32(np, "uaif_max_div", &data->uaif_max_div);
|
|
if (ret < 0) {
|
|
dev_warn(dev, "Failed to read %s: %d\n", "uaif_max_div", ret);
|
|
data->uaif_max_div = 32;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "ipc_tx_offset", &data->ipc_tx_offset);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n", "ipc_tx_offset", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "ipc_rx_offset", &data->ipc_rx_offset);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n", "ipc_rx_offset", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "ipc_tx_ack_offset",
|
|
&data->ipc_tx_ack_offset);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n", "ipc_tx_ack_offset",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "ipc_rx_ack_offset",
|
|
&data->ipc_rx_ack_offset);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n", "ipc_rx_ack_offset",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "pm_qos_int", data->pm_qos_int,
|
|
ARRAY_SIZE(data->pm_qos_int));
|
|
if (ret < 0)
|
|
dev_warn(dev, "Failed to read %s: %d\n", "pm_qos_int", ret);
|
|
|
|
ret = of_property_read_u32_array(np, "pm_qos_aud", data->pm_qos_aud,
|
|
ARRAY_SIZE(data->pm_qos_aud));
|
|
if (ret < 0) {
|
|
dev_warn(dev, "Failed to read %s: %d\n", "pm_qos_aud", ret);
|
|
} else {
|
|
for (i = 0; i < ARRAY_SIZE(data->pm_qos_aud); i++) {
|
|
if (!data->pm_qos_aud[i]) {
|
|
data->cpu_gear_min = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
np_tmp = of_parse_phandle(np, "abox_gic", 0);
|
|
if (!np_tmp) {
|
|
dev_err(dev, "Failed to get abox_gic device node\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
pdev_tmp = of_find_device_by_node(np_tmp);
|
|
if (!pdev_tmp) {
|
|
dev_err(dev, "Failed to get abox_gic platform device\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
data->dev_gic = &pdev_tmp->dev;
|
|
|
|
data->bootargs_offset = 0;
|
|
ret = of_property_read_u32(np, "samsung,abox-bootargs-offset",
|
|
&data->bootargs_offset);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n",
|
|
"samsung,abox-bootargs-offset", ret);
|
|
data->bootargs_offset = 0;
|
|
}
|
|
|
|
ret = of_property_read_string(np, "samsung,abox-bootargs",
|
|
&data->bootargs);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to read %s: %d\n",
|
|
"samsung,abox-bootargs", ret);
|
|
}
|
|
|
|
dev_info(dev, "bootargs[0x%x][%s]\n",
|
|
data->bootargs_offset, data->bootargs);
|
|
|
|
if (abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM)) {
|
|
np_tmp = of_parse_phandle(np, "vts", 0);
|
|
if (!np_tmp) {
|
|
dev_err(dev, "Failed to get vts device node\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
data->pdev_vts = of_find_device_by_node(np_tmp);
|
|
if (!data->pdev_vts) {
|
|
dev_err(dev, "Failed to get vts platform device\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
}
|
|
|
|
#ifdef EMULATOR
|
|
pmu_alive = ioremap(0x16480000, 0x10000);
|
|
#endif
|
|
pm_qos_add_request(&abox_pm_qos_aud, PM_QOS_AUD_THROUGHPUT, 0);
|
|
pm_qos_add_request(&abox_pm_qos_int, PM_QOS_DEVICE_THROUGHPUT, 0);
|
|
pm_qos_add_request(&abox_pm_qos_mif, PM_QOS_BUS_THROUGHPUT, 0);
|
|
pm_qos_add_request(&abox_pm_qos_lit, PM_QOS_CLUSTER0_FREQ_MIN, 0);
|
|
pm_qos_add_request(&abox_pm_qos_big, PM_QOS_CLUSTER1_FREQ_MIN, 0);
|
|
|
|
for (i = 0; i < ABOX_GIC_IRQ_COUNT; i++)
|
|
abox_gic_register_irq_handler(data->dev_gic, i,
|
|
abox_irq_handler, pdev);
|
|
|
|
if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) {
|
|
abox_regmap_config.reg_defaults = abox_reg_defaults_8895;
|
|
abox_regmap_config.num_reg_defaults =
|
|
ARRAY_SIZE(abox_reg_defaults_8895);
|
|
} else if (IS_ENABLED(CONFIG_SOC_EXYNOS9810)) {
|
|
abox_regmap_config.reg_defaults = abox_reg_defaults_9810;
|
|
abox_regmap_config.num_reg_defaults =
|
|
ARRAY_SIZE(abox_reg_defaults_9810);
|
|
} else if (IS_ENABLED(CONFIG_SOC_EXYNOS9610)) {
|
|
abox_regmap_config.reg_defaults = abox_reg_defaults_9610;
|
|
abox_regmap_config.num_reg_defaults =
|
|
ARRAY_SIZE(abox_reg_defaults_9610);
|
|
}
|
|
data->regmap = devm_regmap_init_mmio(dev,
|
|
data->sfr_base,
|
|
&abox_regmap_config);
|
|
|
|
pm_runtime_enable(dev);
|
|
pm_runtime_set_autosuspend_delay(dev, 1);
|
|
pm_runtime_use_autosuspend(dev);
|
|
pm_runtime_get(dev);
|
|
|
|
data->qos_nb.notifier_call = abox_qos_notifier;
|
|
pm_qos_add_notifier(PM_QOS_AUD_THROUGHPUT, &data->qos_nb);
|
|
|
|
data->pm_nb.notifier_call = abox_pm_notifier;
|
|
register_pm_notifier(&data->pm_nb);
|
|
|
|
data->modem_nb.notifier_call = abox_modem_notifier;
|
|
register_modem_event_notifier(&data->modem_nb);
|
|
|
|
#ifdef CONFIG_EXYNOS_ITMON
|
|
data->itmon_nb.notifier_call = abox_itmon_notifier;
|
|
itmon_notifier_chain_register(&data->itmon_nb);
|
|
#endif
|
|
|
|
abox_failsafe_init(dev);
|
|
|
|
wakeup_source_init(&data->ws, "abox");
|
|
wakeup_source_init(&data->ws_boot, "abox_boot");
|
|
|
|
ret = device_create_file(dev, &dev_attr_calliope_version);
|
|
if (ret < 0)
|
|
dev_warn(dev, "Failed to create file: %s\n", "version");
|
|
|
|
ret = device_create_file(dev, &dev_attr_calliope_debug);
|
|
if (ret < 0)
|
|
dev_warn(dev, "Failed to create file: %s\n", "debug");
|
|
|
|
ret = device_create_file(dev, &dev_attr_calliope_cmd);
|
|
if (ret < 0)
|
|
dev_warn(dev, "Failed to create file: %s\n", "cmd");
|
|
|
|
atomic_notifier_chain_register(&panic_notifier_list,
|
|
&abox_panic_notifier);
|
|
|
|
ret = snd_soc_register_component(dev, &abox_cmpnt, abox_dais,
|
|
ARRAY_SIZE(abox_dais));
|
|
if (ret < 0)
|
|
dev_err(dev, "component register failed:%d\n", ret);
|
|
|
|
dev_info(dev, "%s: probe complete\n", __func__);
|
|
|
|
of_platform_populate(np, NULL, NULL, dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int samsung_abox_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct abox_data *data = platform_get_drvdata(pdev);
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
|
|
pm_runtime_disable(dev);
|
|
#ifndef CONFIG_PM
|
|
abox_runtime_suspend(dev);
|
|
#endif
|
|
device_init_wakeup(dev, false);
|
|
destroy_workqueue(data->ipc_workqueue);
|
|
pm_qos_remove_request(&abox_pm_qos_aud);
|
|
pm_qos_remove_request(&abox_pm_qos_int);
|
|
pm_qos_remove_request(&abox_pm_qos_mif);
|
|
pm_qos_remove_request(&abox_pm_qos_lit);
|
|
pm_qos_remove_request(&abox_pm_qos_big);
|
|
snd_soc_unregister_component(dev);
|
|
abox_iommu_unmap(dev, IOVA_DRAM_FIRMWARE);
|
|
#ifdef EMULATOR
|
|
iounmap(pmu_alive);
|
|
#endif
|
|
wakeup_source_trash(&data->ws);
|
|
wakeup_source_trash(&data->ws_boot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void samsung_abox_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
|
|
dev_info(dev, "%s\n", __func__);
|
|
pm_runtime_disable(dev);
|
|
}
|
|
|
|
static const struct of_device_id samsung_abox_match[] = {
|
|
{
|
|
.compatible = "samsung,abox",
|
|
},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, samsung_abox_match);
|
|
|
|
static const struct dev_pm_ops samsung_abox_pm = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(abox_suspend, abox_resume)
|
|
SET_RUNTIME_PM_OPS(abox_runtime_suspend, abox_runtime_resume, NULL)
|
|
};
|
|
|
|
static struct platform_driver samsung_abox_driver = {
|
|
.probe = samsung_abox_probe,
|
|
.remove = samsung_abox_remove,
|
|
.shutdown = samsung_abox_shutdown,
|
|
.driver = {
|
|
.name = "samsung-abox",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(samsung_abox_match),
|
|
.pm = &samsung_abox_pm,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(samsung_abox_driver);
|
|
|
|
static int __init samsung_abox_late_initcall(void)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (p_abox_data && p_abox_data->pdev) {
|
|
if (!abox_test_quirk(p_abox_data, ABOX_QUIRK_OFF_ON_SUSPEND))
|
|
pm_runtime_put(&p_abox_data->pdev->dev);
|
|
} else {
|
|
pr_err("%s: p_abox_data or pdev is null", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
late_initcall(samsung_abox_late_initcall);
|
|
|
|
/* Module information */
|
|
MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
|
|
MODULE_DESCRIPTION("Samsung ASoC A-Box Driver");
|
|
MODULE_ALIAS("platform:samsung-abox");
|
|
MODULE_LICENSE("GPL");
|