blob: f903e00c81ab73e9ca2c217aca17e615f749225d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Google Whitechapel AoC ALSA Machine Driver
*
* Copyright (c) 2019 Google LLC
*
* 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.
*/
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <linux/input.h>
#include <linux/version.h>
#include "aoc_alsa_drv.h"
#include "aoc_alsa.h"
#include "google-aoc-enum.h"
static const char *aoc_detect[] = {
"aoc_audio_state",
"aoc_aocdump_state",
};
extern int snd_soc_component_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
#define MK_BE_PARAMS(id, fmt, chan, sr) \
[AOC_ID_TO_INDEX(id)] = \
{ .format = fmt, .channel = chan, .rate = sr },
#define MK_TDM_BE_PARAMS(id, fmt, chan, sr, nslot, slotfmt) \
[AOC_ID_TO_INDEX(id)] = { .format = fmt, \
.channel = chan, \
.rate = sr, \
.slot_num = nslot, \
.slot_fmt = slotfmt },
#define MK_HW_SR_CTRL(port, xenum, xget, xput) \
SOC_ENUM_EXT(port " Sample Rate", xenum, xget, xput),
#define MK_HW_FMT_CTRL(port, xenum, xget, xput) \
SOC_ENUM_EXT(port " Format", xenum, xget, xput),
#define MK_HW_CH_CTRL(port, xenum, xget, xput) \
SOC_ENUM_EXT(port " Chan", xenum, xget, xput),
#define MK_HW_SLOT_NUM_CTRL(port, xenum, xget, xput) \
SOC_ENUM_EXT(port " nSlot", xenum, xget, xput),
#define MK_HW_SLOT_FMT_CTRL(port, xenum, xget, xput) \
SOC_ENUM_EXT(port " SlotFmt", xenum, xget, xput),
#define MK_HW_PARAM_CTRLS(port, name) \
static const struct snd_kcontrol_new _##port##_ctrls[] = { \
MK_HW_SR_CTRL(name, enum_sr, aoc_be_sr_get, \
aoc_be_sr_put) \
MK_HW_FMT_CTRL(name, enum_fmt, aoc_be_fmt_get, \
aoc_be_fmt_put) \
MK_HW_CH_CTRL(name, enum_ch, aoc_be_ch_get, \
aoc_be_ch_put) \
}
#define MK_TDM_HW_PARAM_CTRLS(port, name) \
static const struct snd_kcontrol_new _##port##_ctrls[] = { \
MK_HW_SR_CTRL(name, enum_sr, aoc_be_sr_get, \
aoc_be_sr_put) \
MK_HW_FMT_CTRL(name, enum_fmt, aoc_be_fmt_get, \
aoc_be_fmt_put) \
MK_HW_CH_CTRL(name, enum_ch, aoc_be_ch_get, \
aoc_be_ch_put) \
MK_HW_SLOT_NUM_CTRL(name, enum_ch, aoc_slot_num_get, \
aoc_slot_num_put) \
MK_HW_SLOT_FMT_CTRL(name, enum_fmt, \
aoc_slot_fmt_get, aoc_slot_fmt_put) \
}
#define MK_BE_RES_ITEM(port, xops, xfixup) \
[AOC_ID_TO_INDEX(port)] = { \
.ops = xops, \
.fixup = xfixup, \
.num_controls = ARRAY_SIZE(_##port##_ctrls), \
.controls = _##port##_ctrls, \
},
#define MK_STR_MAP(xstr, xval) { .str = xstr, .value = xval },
typedef int (*fixup_fn)(struct snd_soc_pcm_runtime *,
struct snd_pcm_hw_params *);
enum {
SRC_MCLK = 0,
SRC_BCLK,
SRC_PLL,
};
struct clk_ctrl {
u32 src;
u32 fix_clk;
int id;
int srcid;
int in_mul;
int out_mul;
int dai_id;
struct device_node *np;
};
struct dai_link_res_map {
const struct snd_soc_ops *ops;
fixup_fn fixup;
int num_controls;
const struct snd_kcontrol_new *controls;
};
struct be_param_cache {
snd_pcm_format_t format;
u32 channel;
u32 rate;
u32 slot_num;
u32 slot_fmt;
};
struct snd_card_pdata {
struct aoc_chip g_chip;
bool has_jack;
bool jack_init;
u32 jack_be_id;
u32 sys_clk_num;
u32 pll_clk_num;
struct mutex mutex;
struct be_param_cache be_params[PORT_MAX];
struct snd_soc_jack jack;
struct device_node *jack_np;
struct clk_ctrl *sys_clks;
struct clk_ctrl *pll_clks;
};
struct str_to_val {
const char *str;
u32 value;
};
static int i2s_startup(struct snd_pcm_substream *);
static void i2s_shutdown(struct snd_pcm_substream *);
static int i2s_hw_params(struct snd_pcm_substream *,
struct snd_pcm_hw_params *);
static int hw_params_fixup(struct snd_soc_pcm_runtime *,
struct snd_pcm_hw_params *);
static int tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *param);
static const struct snd_soc_ops aoc_i2s_ops = {
.startup = i2s_startup,
.shutdown = i2s_shutdown,
.hw_params = i2s_hw_params,
};
static const struct snd_soc_ops aoc_tdm_ops = {
.startup = i2s_startup,
.shutdown = i2s_shutdown,
.hw_params = tdm_hw_params,
};
static const struct str_to_val clksrc_map[] = {
MK_STR_MAP("MCLK", SRC_MCLK)
MK_STR_MAP("BCLK", SRC_BCLK)
MK_STR_MAP("PLL", SRC_PLL)
};
static const struct str_to_val sr_map[] = {
MK_STR_MAP("SR_8K", 8000)
MK_STR_MAP("SR_11P025K", 11025)
MK_STR_MAP("SR_16K", 16000)
MK_STR_MAP("SR_22P05K", 22050)
MK_STR_MAP("SR_32K", 32000)
MK_STR_MAP("SR_44P1K", 44100)
MK_STR_MAP("SR_48K", 48000)
MK_STR_MAP("SR_88P2K", 88200)
MK_STR_MAP("SR_96K", 96000)
MK_STR_MAP("SR_176P4K", 176400)
MK_STR_MAP("SR_192K", 192000)
};
static const struct str_to_val fmt_map[] = {
MK_STR_MAP("S16_LE", SNDRV_PCM_FORMAT_S16_LE)
MK_STR_MAP("S24_LE", SNDRV_PCM_FORMAT_S24_LE)
MK_STR_MAP("S24_3LE", SNDRV_PCM_FORMAT_S24_3LE)
MK_STR_MAP("S32_LE", SNDRV_PCM_FORMAT_S32_LE)
MK_STR_MAP("FLOAT_LE", SNDRV_PCM_FORMAT_FLOAT_LE)
};
static const struct str_to_val ch_map[] = {
MK_STR_MAP("One", 1)
MK_STR_MAP("Two", 2)
MK_STR_MAP("Three", 3)
MK_STR_MAP("Four", 4)
MK_STR_MAP("Five", 5)
MK_STR_MAP("Six", 6)
MK_STR_MAP("Seven", 7)
MK_STR_MAP("Eight", 8)
};
static const char *sr_text[ARRAY_SIZE(sr_map)] = {};
static const char *fmt_text[ARRAY_SIZE(fmt_map)] = {};
static const char *ch_text[ARRAY_SIZE(ch_map)] = {};
static struct soc_enum enum_sr =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sr_text), sr_text);
static struct soc_enum enum_fmt =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fmt_text), fmt_text);
static struct soc_enum enum_ch =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ch_text), ch_text);
static const struct be_param_cache default_be_params[PORT_MAX] = {
MK_BE_PARAMS(I2S_0_RX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(I2S_0_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(I2S_1_RX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(I2S_1_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(I2S_2_RX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(I2S_2_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_TDM_BE_PARAMS(TDM_0_RX, SNDRV_PCM_FORMAT_S16_LE,
2, 48000, 4, SNDRV_PCM_FORMAT_S32_LE)
MK_TDM_BE_PARAMS(TDM_0_TX, SNDRV_PCM_FORMAT_S16_LE,
2, 48000, 4, SNDRV_PCM_FORMAT_S32_LE)
MK_TDM_BE_PARAMS(TDM_1_RX, SNDRV_PCM_FORMAT_S16_LE,
2, 48000, 4, SNDRV_PCM_FORMAT_S32_LE)
MK_TDM_BE_PARAMS(TDM_1_TX, SNDRV_PCM_FORMAT_S16_LE,
2, 48000, 4, SNDRV_PCM_FORMAT_S32_LE)
MK_BE_PARAMS(INTERNAL_MIC_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(ERASER_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(BT_RX, SNDRV_PCM_FORMAT_S16_LE, 1, 16000)
MK_BE_PARAMS(BT_TX, SNDRV_PCM_FORMAT_S16_LE, 1, 16000)
MK_BE_PARAMS(USB_RX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(USB_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(INCALL_RX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_BE_PARAMS(INCALL_TX, SNDRV_PCM_FORMAT_S16_LE, 2, 48000)
MK_TDM_BE_PARAMS(HAPTIC_RX, SNDRV_PCM_FORMAT_S32_LE,
4, 48000, 4, SNDRV_PCM_FORMAT_S32_LE)
};
static struct snd_soc_dai_link_component null_component = {
.name = "snd-soc-dummy",
.dai_name = "snd-soc-dummy-dai",
};
static __poll_t audio_state_poll(struct snd_info_entry *entry,
void *private_data, struct file *f, poll_table *wait)
{
struct aoc_state_client_t *client = entry->private_data;
return aoc_audio_state_poll(f, wait, client);
}
static ssize_t audio_state_read(struct snd_info_entry *entry,
void *private_data, struct file *file, char __user *buf,
size_t count, loff_t pos)
{
struct aoc_state_client_t *client = entry->private_data;
if (!client || !client->inuse)
return -ENODEV;
if (!buf || count != sizeof(client->online) || pos != 0)
return -EINVAL;
client->online = aoc_audio_current_state();
return copy_to_user(buf, &client->online, sizeof(client->online));
}
static int audio_state_open(struct snd_info_entry *entry,
unsigned short mode, void **private_data)
{
struct aoc_state_client_t *client = entry->private_data;
/* Only allow one client */
if (!client || client->inuse)
return -ENODEV;
client->inuse = true;
client->exit = false;
return 0;
}
static int audio_state_release(struct snd_info_entry *entry,
unsigned short mode, void *private_data)
{
struct aoc_state_client_t *client = entry->private_data;
if (!client)
return -ENODEV;
client->exit = true;
client->inuse = false;
return 0;
}
static struct snd_info_entry_ops audio_state_ops = {
.poll = audio_state_poll,
.read = audio_state_read,
.open = audio_state_open,
.release = audio_state_release,
};
static void audio_state_private_free(struct snd_info_entry *entry)
{
struct aoc_state_client_t *client =
(struct aoc_state_client_t *)entry->private_data;
if (!client)
return;
entry->private_data = NULL;
free_audio_state_client(client);
}
static int hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_mask *fmt_mask =
hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_interval *rate =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct aoc_chip *chip =
(struct aoc_chip *)snd_soc_card_get_drvdata(rtd->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
u32 sr, ch;
snd_pcm_format_t fmt;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid id %u found for %s", __func__, id,
rtd->dai_link->name);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
sr = pdata->be_params[id].rate;
ch = pdata->be_params[id].channel;
fmt = pdata->be_params[id].format;
mutex_unlock(&pdata->mutex);
pr_debug("%s: fixup ch %u rate %u fmt %u for %s", __func__, ch, sr,
fmt, rtd->dai_link->name);
rate->min = rate->max = sr;
channels->min = channels->max = ch;
snd_mask_none(fmt_mask);
snd_mask_set_format(fmt_mask, fmt);
return 0;
}
static int i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
pr_debug("%s: %s dai_fmt = 0x%x\n", __func__,
dai_link->name, dai_link->dai_fmt);
return snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
}
static void i2s_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
pr_debug("%s close\n", dai_link->name);
}
static struct clk_ctrl *find_clk(struct snd_soc_dai *dai, int clk_num,
struct clk_ctrl *clks)
{
struct clk_ctrl *clk = NULL;
struct device_node *np;
int i;
if (!clks || !dai)
return clk;
np = dai->component->dev->of_node;
for (i = 0; i < clk_num; i++, clks++) {
if ((np && clks->np == np) || clks->dai_id == dai->id) {
clk = clks;
break;
}
}
return clk;
}
static void set_pll_clk(struct snd_soc_dai *dai, u32 bclk, u32 rate,
struct snd_card_pdata *pdata)
{
struct clk_ctrl *pll;
int ret;
u32 clk;
pll = find_clk(dai, pdata->pll_clk_num, pdata->pll_clks);
if (!pll)
return;
if (pll->fix_clk)
clk = pll->fix_clk;
else {
if (pll->src == SRC_MCLK)
clk = rate;
else
clk = bclk;
}
pr_debug("%s: %s pll %u src %u freq_in %u freq_out %u", __func__,
dai->name, pll->id, pll->srcid, clk * pll->in_mul,
clk * pll->out_mul);
ret = snd_soc_dai_set_pll(dai, pll->id, pll->srcid,
clk * pll->in_mul, clk * pll->out_mul);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set codec_dai pll %s fail %d",
__func__, dai->name, ret);
ret = snd_soc_component_set_pll(dai->component, pll->id, pll->srcid,
clk * pll->in_mul, clk * pll->out_mul);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set codec pll %s fail %d",
__func__, dai->name, ret);
}
static void set_sys_clk(struct snd_soc_dai *dai, u32 bclk, u32 rate,
u32 dir, struct snd_card_pdata *pdata)
{
struct clk_ctrl *sys;
int ret;
u32 clk;
sys = find_clk(dai, pdata->sys_clk_num, pdata->sys_clks);
if (!sys)
return;
if (sys->fix_clk)
clk = sys->fix_clk;
else {
if (sys->src == SRC_MCLK)
clk = rate;
else
clk = bclk;
}
if (dir == SND_SOC_CLOCK_IN)
clk *= sys->in_mul;
else
clk *= sys->out_mul;
pr_debug("%s: %s clkid %u clk %u", __func__,
dai->name, sys->id, clk);
ret = snd_soc_dai_set_sysclk(dai, sys->id, clk, dir);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set codec_dai clk %s fail %d",
__func__, dai->name, ret);
ret = snd_soc_component_set_sysclk(dai->component,
sys->id, sys->srcid, clk, dir);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set codec sys clk %s fail %d",
__func__, dai->name, ret);
}
static int i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *param)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
u32 rate, bclk, channel;
int i, bit_width, ret;
struct aoc_chip *chip =
(struct aoc_chip *)snd_soc_card_get_drvdata(rtd->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
bit_width = params_physical_width(param);
if (bit_width < 0) {
pr_err("%s: invalid bit width %d", __func__, bit_width);
return -EINVAL;
}
channel = params_channels(param);
rate = params_rate(param);
bclk = rate * ((u32)bit_width) * channel;
pr_debug("%sv2: ch %u rate %d bit %d", __func__,
channel, rate, bit_width);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0,
channel, bit_width);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set tdm slot %s fail %d", __func__,
cpu_dai->name, ret);
set_sys_clk(cpu_dai, bclk, rate, SND_SOC_CLOCK_OUT, pdata);
}
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0,
channel, bit_width);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set tdm slot %s fail %d", __func__,
cpu_dai->name, ret);
set_pll_clk(codec_dai, bclk, rate, pdata);
set_sys_clk(codec_dai, bclk, rate, SND_SOC_CLOCK_IN, pdata);
}
return 0;
}
static int tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *param)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
u32 rate, bclk, channel, tdmslot;
int i, bit_width, ret, slot_width;
snd_pcm_format_t format;
u32 idx = AOC_ID_TO_INDEX(cpu_dai->id);
struct aoc_chip *chip =
(struct aoc_chip *)snd_soc_card_get_drvdata(rtd->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
if (idx >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid id %u found for %s", __func__, idx,
dai_link->name);
return -EINVAL;
}
bit_width = params_physical_width(param);
if (bit_width < 0) {
pr_err("%s: invalid bit width %d", __func__, bit_width);
return -EINVAL;
}
channel = params_channels(param);
switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* I2S mode */
tdmslot = channel;
slot_width = bit_width;
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
/* TDM mode */
tdmslot = pdata->be_params[idx].slot_num;
format = pdata->be_params[idx].slot_fmt;
slot_width = snd_pcm_format_physical_width(format);
if (tdmslot < channel || slot_width < bit_width) {
pr_err("%s: inval ch %u slot %u, bit %d, slot_bit %d",
__func__, channel, tdmslot,
bit_width, slot_width);
return -EINVAL;
}
break;
default:
pr_err("%s: unsupport fmt %u on %s", __func__,
dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK,
dai_link->name);
return -EINVAL;
}
rate = params_rate(param);
bclk = rate * ((u32)slot_width) * tdmslot;
pr_debug("%s:ch %u tdm slot %u bit %d, slot_bit %d", __func__,
channel, tdmslot, bit_width, slot_width);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0,
tdmslot, slot_width);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set tdm slot %s fail %d", __func__,
cpu_dai->name, ret);
set_sys_clk(cpu_dai, bclk, rate, SND_SOC_CLOCK_OUT, pdata);
}
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0,
tdmslot, slot_width);
if (ret && ret != -ENOTSUPP)
pr_warn("%s: set tdm slot %s fail %d", __func__,
codec_dai->name, ret);
set_pll_clk(codec_dai, bclk, rate, pdata);
set_sys_clk(codec_dai, bclk, rate, SND_SOC_CLOCK_IN, pdata);
}
return 0;
}
static int aoc_slot_num_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = (struct snd_soc_dai *)
snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id), i;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
for (i = 0; i < ARRAY_SIZE(ch_map); i++) {
if (pdata->be_params[id].slot_num == ch_map[i].value) {
break;
}
}
mutex_unlock(&pdata->mutex);
if (i == ARRAY_SIZE(ch_map))
return -EINVAL;
ucontrol->value.integer.value[0] = (int)i;
return 0;
}
static int aoc_slot_num_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
int idx = ucontrol->value.integer.value[0];
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
if (idx < 0 || idx >= ARRAY_SIZE(ch_map)) {
pr_err("%s: invalid idx %d", __func__, idx);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
pdata->be_params[id].slot_num = ch_map[idx].value;
mutex_unlock(&pdata->mutex);
return 0;
}
static int aoc_slot_fmt_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id), i;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
for (i = 0; i < ARRAY_SIZE(fmt_map); i++) {
if (pdata->be_params[id].slot_fmt == fmt_map[i].value) {
break;
}
}
mutex_unlock(&pdata->mutex);
if (i == ARRAY_SIZE(fmt_map))
return -EINVAL;
ucontrol->value.integer.value[0] = (int)i;
return 0;
}
static int aoc_slot_fmt_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
int idx = ucontrol->value.integer.value[0];
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
if (idx < 0 || idx >= ARRAY_SIZE(fmt_map))
return -EINVAL;
mutex_lock(&pdata->mutex);
pdata->be_params[id].slot_fmt = fmt_map[idx].value;
mutex_unlock(&pdata->mutex);
return 0;
}
static int aoc_be_sr_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id), i;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
for (i = 0; i < ARRAY_SIZE(sr_map); i++) {
if (pdata->be_params[id].rate == sr_map[i].value) {
break;
}
}
mutex_unlock(&pdata->mutex);
if (i == ARRAY_SIZE(sr_map))
return -EINVAL;
ucontrol->value.integer.value[0] = (int)i;
return 0;
}
static int aoc_be_sr_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
int idx = ucontrol->value.integer.value[0];
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
if (idx < 0 || idx >= ARRAY_SIZE(sr_map)) {
pr_err("%s: invalid idx %d", __func__, idx);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
pdata->be_params[id].rate = sr_map[idx].value;
mutex_unlock(&pdata->mutex);
return 0;
}
static int aoc_be_fmt_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id), i;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
for (i = 0; i < ARRAY_SIZE(fmt_map); i++) {
if (pdata->be_params[id].format == fmt_map[i].value) {
break;
}
}
mutex_unlock(&pdata->mutex);
if (i == ARRAY_SIZE(fmt_map))
return -EINVAL;
ucontrol->value.integer.value[0] = (int)i;
return 0;
}
static int aoc_be_fmt_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
int idx = ucontrol->value.integer.value[0];
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
if (idx < 0 || idx >= ARRAY_SIZE(fmt_map))
return -EINVAL;
mutex_lock(&pdata->mutex);
pdata->be_params[id].format = fmt_map[idx].value;
mutex_unlock(&pdata->mutex);
return 0;
}
static int aoc_be_ch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id), i;
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
mutex_lock(&pdata->mutex);
for (i = 0; i < ARRAY_SIZE(ch_map); i++) {
if (pdata->be_params[id].channel == ch_map[i].value) {
break;
}
}
mutex_unlock(&pdata->mutex);
if (i == ARRAY_SIZE(ch_map))
return -EINVAL;
ucontrol->value.integer.value[0] = (int)i;
return 0;
}
static int aoc_be_ch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai =
(struct snd_soc_dai *)snd_kcontrol_chip(kcontrol);
struct aoc_chip *chip = (struct aoc_chip *)
snd_soc_card_get_drvdata(cpu_dai->component->card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id = AOC_ID_TO_INDEX(cpu_dai->id);
int idx = ucontrol->value.integer.value[0];
if (id >= ARRAY_SIZE(pdata->be_params)) {
pr_err("%s: invalid idx %u", __func__, id);
return -EINVAL;
}
if (idx < 0 || idx >= ARRAY_SIZE(ch_map))
return -EINVAL;
mutex_lock(&pdata->mutex);
pdata->be_params[id].channel = ch_map[idx].value;
mutex_unlock(&pdata->mutex);
return 0;
}
/*
* Declare the Sample rate, bit width, Channel controls
* of the hardware backend port.
*
* Examples:
* "I2S_0_RX Sample Rate"
* "I2S_0_RX Format"
* "I2S_0_RX Chan"
*/
MK_HW_PARAM_CTRLS(I2S_0_RX, "I2S_0_RX");
MK_HW_PARAM_CTRLS(I2S_0_TX, "I2S_0_TX");
MK_HW_PARAM_CTRLS(I2S_1_RX, "I2S_1_RX");
MK_HW_PARAM_CTRLS(I2S_1_TX, "I2S_1_TX");
MK_HW_PARAM_CTRLS(I2S_2_RX, "I2S_2_RX");
MK_HW_PARAM_CTRLS(I2S_2_TX, "I2S_2_TX");
MK_TDM_HW_PARAM_CTRLS(TDM_0_RX, "TDM_0_RX");
MK_TDM_HW_PARAM_CTRLS(TDM_0_TX, "TDM_0_TX");
MK_TDM_HW_PARAM_CTRLS(TDM_1_RX, "TDM_1_RX");
MK_TDM_HW_PARAM_CTRLS(TDM_1_TX, "TDM_1_TX");
MK_HW_PARAM_CTRLS(INTERNAL_MIC_TX, "INTERNAL_MIC_TX");
MK_HW_PARAM_CTRLS(ERASER_TX, "ERASER_TX");
MK_HW_PARAM_CTRLS(BT_RX, "BT_RX");
MK_HW_PARAM_CTRLS(BT_TX, "BT_TX");
MK_HW_PARAM_CTRLS(USB_RX, "USB_RX");
MK_HW_PARAM_CTRLS(USB_TX, "USB_TX");
MK_HW_PARAM_CTRLS(INCALL_RX, "INCALL_RX");
MK_HW_PARAM_CTRLS(INCALL_TX, "INCALL_TX");
MK_TDM_HW_PARAM_CTRLS(HAPTIC_RX, "HAPTIC_RX");
/*
* The resource array that have ALSA controls, ops and fixup
* funciton of each backend port.
*
*/
static const struct dai_link_res_map be_res_map[PORT_MAX] = {
MK_BE_RES_ITEM(I2S_0_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(I2S_0_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(I2S_1_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(I2S_1_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(I2S_2_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(I2S_2_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(TDM_0_RX, &aoc_tdm_ops, hw_params_fixup)
MK_BE_RES_ITEM(TDM_0_TX, &aoc_tdm_ops, hw_params_fixup)
MK_BE_RES_ITEM(TDM_1_RX, &aoc_tdm_ops, hw_params_fixup)
MK_BE_RES_ITEM(TDM_1_TX, &aoc_tdm_ops, hw_params_fixup)
MK_BE_RES_ITEM(INTERNAL_MIC_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(ERASER_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(BT_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(BT_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(USB_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(USB_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(INCALL_RX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(INCALL_TX, &aoc_i2s_ops, hw_params_fixup)
MK_BE_RES_ITEM(HAPTIC_RX, &aoc_tdm_ops, hw_params_fixup)
};
static void put_component(struct snd_soc_dai_link_component *component,
unsigned int number)
{
uint32_t i;
if (!component)
return;
for (i = 0; i < number; i++, component++) {
if (!component->of_node)
continue;
of_node_put(component->of_node);
component->of_node = NULL;
}
}
static void put_dai_links(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link;
uint32_t i;
if (!card || !card->dai_link || !card->num_links)
return;
dai_link = card->dai_link;
for (i = 0; i < card->num_links; i++, dai_link++) {
put_component(dai_link->cpus, dai_link->num_cpus);
put_component(dai_link->codecs, dai_link->num_codecs);
put_component(dai_link->platforms, dai_link->num_platforms);
}
}
static int of_parse_dai_platform(struct device *dev,
struct device_node *node, struct snd_soc_dai_link *dai)
{
struct device_node *of_platform_root = NULL;
struct device_node *of_node;
struct device_node *np;
int count, ret = 0;
struct snd_soc_dai_link_component *component;
/*
* The platform is not must have
*/
of_platform_root = of_get_child_by_name(node, "platform");
if (!of_platform_root)
return 0;
count = of_get_available_child_count(of_platform_root);
if (count <= 0) {
pr_err("invalid child count %d for %s\n", count,
of_platform_root->name);
ret = -EINVAL;
goto exit;
}
component = devm_kzalloc(dev,
sizeof(struct snd_soc_dai_link_component) * count, GFP_KERNEL);
if (!component) {
ret = -ENOMEM;
goto exit;
}
dai->platforms = component;
dai->num_platforms = count;
count = 0;
for_each_available_child_of_node(of_platform_root, np) {
of_node = of_parse_phandle(np, "of_drv", 0);
if (!of_node) {
pr_err("%s: no of_drv for %s", __func__,
of_node->name);
ret = -EINVAL;
break;
}
component->of_node = of_node;
component++;
count++;
}
exit:
if (of_platform_root)
of_node_put(of_platform_root);
return ret;
}
static int of_parse_dai_cpu(struct device *dev,
struct device_node *node, struct snd_soc_dai_link *dai)
{
struct device_node *of_cpu_root = NULL, *of_node;
struct snd_soc_dai_link_component *component;
int ret;
/*
* Each FE/BE must specify the cpu dai
*/
of_cpu_root = of_get_child_by_name(node, "cpu");
if (!of_cpu_root) {
pr_err("%s: can't find cpu node for %s", __func__, dai->name);
return -EINVAL;
}
of_node = of_parse_phandle(of_cpu_root, "sound-dai", 0);
if (!of_node) {
pr_err("%s: fail to get cpu dai for %s", __func__, dai->name);
ret = -EINVAL;
goto exit;
}
component = devm_kzalloc(dev,
sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
if (!component) {
ret = -ENOMEM;
goto exit;
}
/* Only support single cpu dai */
dai->cpus = component;
dai->num_cpus = 1;
component->of_node = of_node;
ret = snd_soc_of_get_dai_name(of_cpu_root, &component->dai_name);
if (ret) {
if (ret == -EPROBE_DEFER) {
pr_info("%s: wait cpu_dai for %s", __func__, dai->name);
} else {
pr_err("%s: get cpu_dai fail for %s", __func__,
dai->name);
}
}
exit:
if (of_cpu_root)
of_node_put(of_cpu_root);
return ret;
}
static int of_parse_dai_codec(struct device *dev,
struct device_node *node, struct snd_soc_dai_link *dai)
{
struct device_node *of_codec_root;
int ret;
of_codec_root = of_get_child_by_name(node, "codec");
if (!of_codec_root) {
/* default codec */
dai->codecs = &null_component;
dai->num_codecs = 1;
return 0;
}
ret = snd_soc_of_get_dai_link_codecs(dev, of_codec_root, dai);
of_node_put(of_codec_root);
return ret;
}
static int of_parse_one_dai(struct device_node *node, struct device *dev,
struct snd_soc_dai_link *dai)
{
int ret = 0;
bool ops, fixup;
u32 trigger, id;
struct device_node *daifmt = NULL;
if (!node || !dai)
return -EINVAL;
ret = of_property_read_string(node, "dai-name", &dai->name);
if (ret) {
pr_err("%s: fail to get dai name %d", __func__, ret);
goto exit;
}
ret = of_property_read_string(node, "stream-name", &dai->stream_name);
if (ret) {
pr_err("%s: fail to get dai stream name %d", __func__, ret);
goto exit;
}
ret = of_parse_dai_cpu(dev, node, dai);
if (ret) {
pr_err("%s: fail to parse cpu %d for %s", __func__, ret, dai->name);
goto exit;
}
ret = of_parse_dai_platform(dev, node, dai);
if (ret) {
pr_err("%s: fail to parse platform %d for %s", __func__, ret, dai->name);
goto exit;
}
ret = of_parse_dai_codec(dev, node, dai);
if (ret) {
pr_err("%s: fail to parse codec %d for %s", __func__, ret, dai->name);
goto exit;
}
ret = of_property_read_u32_index(node, "trigger", 0, &trigger);
if (ret == 0) {
switch (trigger) {
case 1:
dai->trigger[0] = SND_SOC_DPCM_TRIGGER_POST;
dai->trigger[1] = SND_SOC_DPCM_TRIGGER_POST;
break;
case 2:
dai->trigger[0] = SND_SOC_DPCM_TRIGGER_BESPOKE;
dai->trigger[1] = SND_SOC_DPCM_TRIGGER_BESPOKE;
break;
default:
dai->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
dai->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
break;
}
} else {
/* default setting */
dai->trigger[0] = SND_SOC_DPCM_TRIGGER_POST;
dai->trigger[1] = SND_SOC_DPCM_TRIGGER_POST;
}
ret = of_property_read_u32_index(node, "id", 0, &id);
if (ret == 0) {
dai->id = id;
id = AOC_ID_TO_INDEX(id);
if (dai->id & AOC_BE) {
if (id < ARRAY_SIZE(be_res_map)) {
ops = of_property_read_bool(node, "useops");
fixup = of_property_read_bool(node, "usefixup");
if (ops)
dai->ops = be_res_map[id].ops;
if (fixup)
dai->be_hw_params_fixup =
be_res_map[id].fixup;
}
}
}
daifmt = of_get_child_by_name(node, "daifmt");
if (daifmt) {
dai->dai_fmt =
snd_soc_of_parse_daifmt(daifmt, NULL, NULL, NULL);
of_node_put(daifmt);
pr_debug("%s: daifmt 0x%x for %s", __func__, dai->dai_fmt,
dai->name);
}
dai->dpcm_playback = of_property_read_bool(node, "playback");
dai->dpcm_capture = of_property_read_bool(node, "capture");
dai->no_pcm = of_property_read_bool(node, "no-pcm");
dai->dynamic = of_property_read_bool(node, "dynamic");
dai->ignore_pmdown_time =
!of_property_read_bool(node, "require-pmdown-time");
dai->ignore_suspend = !of_property_read_bool(node, "require-suspend");
exit:
return ret;
}
static int aoc_of_parse_dai_link(struct device_node *node,
struct snd_soc_card *card)
{
int ret = 0, count;
struct device_node *np_dai;
struct device_node *np = NULL;
struct device *dev = card->dev;
struct snd_soc_dai_link *dai_link;
np_dai = of_get_child_by_name(node, "dai_link");
if (!np_dai) {
pr_err("%s: can't find dai-link node", __func__);
return -EINVAL;
}
count = (int)of_get_available_child_count(np_dai);
if (count <= 0) {
pr_err("%s: count %d invalid", __func__, count);
ret = -EINVAL;
goto err;
}
dai_link = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * count,
GFP_KERNEL);
if (!dai_link) {
pr_err("%s: fail to allocate memory for dai_link", __func__);
ret = -ENOMEM;
goto err;
}
card->num_links = count;
card->dai_link = dai_link;
count = 0;
for_each_available_child_of_node (np_dai, np) {
if (count >= card->num_links) {
pr_err("%s: dai link num is full %u", __func__, count);
break;
}
ret = of_parse_one_dai(np, card->dev, dai_link);
if (ret) {
if (ret == -EPROBE_DEFER) {
pr_info("%s: register sound card later",
__func__);
} else {
/*
* If any dai link error, then
* sound card should be fail
*/
pr_err("%s: fail to parse %s", __func__,
np->name);
}
break;
}
#ifdef DUMP_DAI_LINK_INFO
pr_info("dai: %s\n", dai_link->name);
pr_info("id: %u\n", (uint32_t)dai_link->id);
pr_info("playback %u capture %u\n", dai_link->dpcm_playback,
dai_link->dpcm_capture);
pr_info("no-pcm: %u\n", dai_link->no_pcm);
pr_info("dynamic: %u\n", dai_link->dynamic);
pr_info("\n");
#endif
dai_link++;
count++;
}
if (ret < 0)
goto err;
card->num_links = count;
of_node_put(np_dai);
return ret;
err:
put_dai_links(card);
of_node_put(np_dai);
return ret;
}
static int of_parse_one_codec_cfg(struct device_node *node,
struct snd_soc_codec_conf *codec_cfg)
{
int ret = 0;
struct device_node *of_node;
if (!node || !codec_cfg)
return -EINVAL;
of_node = of_parse_phandle(node, "of_node", 0);
if (!of_node) {
pr_err("%s: fail to get of_node for %s", __func__, node->name);
ret = -EINVAL;
goto err;
}
codec_cfg->dlc.of_node = of_node;
ret = of_property_read_string(node, "prefix", &codec_cfg->name_prefix);
if (ret) {
pr_err("%s: fail to get prefix for %s %d", __func__, node->name,
ret);
goto err;
}
return 0;
err:
return ret;
}
static int aoc_of_parse_codec_conf(struct device_node *node,
struct snd_soc_card *card)
{
int ret = 0, count;
struct device_node *np_cfg;
struct device_node *np = NULL;
struct snd_soc_codec_conf *codec_cfg;
struct device *dev = card->dev;
np_cfg = of_get_child_by_name(node, "codec_cfg");
if (!np_cfg) {
pr_info("%s: can't find codec cfg node", __func__);
return 0;
}
count = (int)of_get_available_child_count(np_cfg);
if (count <= 0) {
pr_err("%s: count %d invalid", __func__, count);
ret = -EINVAL;
goto err;
}
codec_cfg = devm_kzalloc(dev, sizeof(struct snd_soc_codec_conf) * count,
GFP_KERNEL);
if (!codec_cfg) {
pr_err("%s: fail to allocate memory for codec_cfg", __func__);
ret = -ENOMEM;
goto err;
}
card->num_configs = count;
card->codec_conf = codec_cfg;
count = 0;
for_each_available_child_of_node (np_cfg, np) {
if (count >= card->num_configs) {
pr_err("%s: conf num is full %u", __func__, count);
break;
}
ret = of_parse_one_codec_cfg(np, codec_cfg);
if (ret) {
memset(codec_cfg, 0, sizeof(*codec_cfg));
continue;
}
codec_cfg++;
count++;
}
card->num_configs = count;
ret = 0;
err:
of_node_put(np_cfg);
return ret;
}
static int aoc_of_parse_hs_jack(struct device_node *node,
struct snd_card_pdata *pdata)
{
struct device_node *np_cfg;
int ret;
np_cfg = of_get_child_by_name(node, "hs_jack");
if (!np_cfg) {
pr_info("%s: no hs jack", __func__);
return 0;
}
ret = of_property_read_u32_index(np_cfg, "be_id",
0, &pdata->jack_be_id);
if (ret != 0) {
pr_err("%s: fail to parse id %d\n", __func__, ret);
goto err_exit;
}
pdata->jack_np = of_parse_phandle(np_cfg, "codec", 0);
if (!pdata->jack_np) {
pr_err("%s: fail to codec np\n", __func__);
goto err_exit;
}
pdata->has_jack = true;
return 0;
err_exit:
of_node_put(np_cfg);
return ret;
}
static int aoc_of_parse_clk(struct device_node *np_clk,
struct snd_soc_card *card, u32 *clk_num, struct clk_ctrl **clks)
{
struct device *dev = card->dev;
struct device_node *np;
struct clk_ctrl *cur;
int count, ret = 0, i;
const char *clk_type = NULL;
u32 fixclk;
if (!clks || !clk_num || !np_clk)
return -EINVAL;
*clk_num = 0;
count = (int)of_get_available_child_count(np_clk);
if (count <= 0)
return ret;
*clks = devm_kzalloc(dev, sizeof(struct clk_ctrl) * count, GFP_KERNEL);
if (!(*clks)) {
pr_err("parse_clk: fail to alloc mem");
return -ENOMEM;
}
cur = *clks;
for_each_available_child_of_node(np_clk, np) {
if (*clk_num >= count) {
pr_err("%s: %s clk number overflow %d %d\n",
__func__, np->name, *clk_num, count);
ret = -EINVAL;
goto err_exit;
}
cur->np = of_parse_phandle(np, "comp", 0);
if (!cur->np) {
ret = of_property_read_u32(np, "dai_id", &cur->dai_id);
if (ret) {
pr_err("%s: %s fail to parse comp\n",
__func__, np->name);
goto err_exit;
}
} else {
/*
* If the of_node exists, then set the dai_id to
* be 0xFFFFFFFF
*/
cur->dai_id = 0xFFFFFFFF;
}
ret = of_property_read_string(np, "src", &clk_type);
if (ret) {
pr_err("%s: %s fail to clk type %d",
__func__, np->name, ret);
goto err_exit;
}
if (!clk_type) {
pr_err("%s: %s clk_type is NULL", __func__, np->name);
ret = -EINVAL;
goto err_exit;
}
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(clksrc_map); i++) {
if (!strcmp(clk_type, clksrc_map[i].str)) {
ret = 0;
cur->src = clksrc_map[i].value;
break;
}
}
if (ret) {
pr_err("%s: %s fail to convert type %s",
__func__, np->name, clk_type);
goto err_exit;
}
ret = of_property_read_u32(np, "id", &cur->id);
if (ret != 0) {
pr_err("%s: %s fail to parse id %d\n",
__func__, np->name, ret);
goto err_exit;
}
ret = of_property_read_u32(np, "srcid", &cur->srcid);
if (ret != 0) {
pr_err("%s: %s fail to parse srcid %d\n",
__func__, np->name, ret);
goto err_exit;
}
ret = of_property_read_u32(np, "in_mul", &cur->in_mul);
if (ret != 0) {
pr_err("%s: %s fail to parse in_mul %d\n",
__func__, np->name, ret);
goto err_exit;
}
ret = of_property_read_u32(np, "out_mul", &cur->out_mul);
if (ret != 0) {
pr_err("%s: %s fail to parse out_mul %d\n",
__func__, np->name, ret);
goto err_exit;
}
ret = of_property_read_u32(np, "fixclk", &fixclk);
if (ret == 0)
cur->fix_clk = fixclk;
if (cur->src == SRC_PLL && !cur->fix_clk) {
pr_err("%s: %s PLL requires fixup clk\n",
__func__, np->name);
goto err_exit;
}
(*clk_num)++;
cur++;
}
return 0;
err_exit:
*clk_num = 0;
devm_kfree(dev, *clks);
*clks = NULL;
return ret;
}
static int aoc_of_parse_clks(struct device_node *node,
struct snd_soc_card *card, struct snd_card_pdata *pdata)
{
struct device_node *np_clks, *cur;
int ret = 0;
np_clks = of_get_child_by_name(node, "clks");
if (!np_clks) {
pr_info("%s: no clks", __func__);
return ret;
}
/* Parse the sys clock */
cur = of_get_child_by_name(np_clks, "sys");
if (cur) {
ret = aoc_of_parse_clk(cur, card, &pdata->sys_clk_num,
&pdata->sys_clks);
of_node_put(cur);
if (ret < 0) {
pr_err("%s: fail to parse sysclk %d", __func__, ret);
goto err_exit;
}
}
/* Parse the pll clock */
cur = of_get_child_by_name(np_clks, "pll");
if (cur) {
ret = aoc_of_parse_clk(cur, card, &pdata->pll_clk_num,
&pdata->pll_clks);
of_node_put(cur);
if (ret < 0) {
pr_err("%s: fail to parse sysclk %d", __func__, ret);
goto err_exit;
}
}
err_exit:
of_node_put(np_clks);
return ret;
}
static int aoc_snd_card_parse_of(struct device_node *node,
struct snd_soc_card *card, struct snd_card_pdata *pdata)
{
int ret;
ret = aoc_of_parse_dai_link(node, card);
if (ret) {
pr_err("%s: fail to parse fai_link %d", __func__, ret);
goto err;
}
ret = aoc_of_parse_codec_conf(node, card);
if (ret) {
pr_err("%s: fail to parse codec conf %d", __func__, ret);
goto err;
}
ret = aoc_of_parse_hs_jack(node, pdata);
if (ret) {
pr_err("%s: fail to parse hs jack %d", __func__, ret);
goto err;
}
ret = aoc_of_parse_clks(node, card, pdata);
if (ret) {
pr_err("%s: fail to parse clks %d", __func__, ret);
goto err;
}
ret = snd_soc_of_parse_card_name(card, "aoc-card-name");
if (ret) {
pr_err("%s: fail to parse snd card name %d", __func__, ret);
goto err;
}
err:
return ret;
}
static void init_audio_state_query(struct snd_soc_card *card, const char *name)
{
struct snd_info_entry *entry = NULL;
struct aoc_state_client_t *client;
client = alloc_audio_state_client();
if (!client) {
pr_err("fail to allocate %s client\n", name);
return;
}
snd_card_proc_new(card->snd_card, name, &entry);
if (!entry) {
pr_warn("%s: fail to create entry %s\n", __func__, name);
free_audio_state_client(client);
return;
}
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = client;
entry->c.ops = &audio_state_ops;
entry->private_free = audio_state_private_free;
entry->size = sizeof(client->online);
}
static void init_headset_jack(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd, u32 id)
{
struct snd_soc_dai *codec_dai, *target_dai = NULL;
struct aoc_chip *chip =
(struct aoc_chip *)snd_soc_card_get_drvdata(card);
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
int i, err;
if (!pdata || !pdata->has_jack || pdata->jack_init ||
pdata->jack_be_id != id)
return;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (pdata->jack_np ==
codec_dai->component->dev->of_node) {
target_dai = codec_dai;
break;
}
}
if (!target_dai) {
pr_err("Fail to find target_dai for headset jack\n");
return;
}
/* setup hs jack */
err = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3, &pdata->jack, NULL, 0);
if (err) {
pr_err("Fail to create headset jack %d\n",
err);
return;
}
snd_jack_set_key(pdata->jack.jack,
SND_JACK_BTN_0, KEY_MEDIA);
snd_jack_set_key(pdata->jack.jack,
SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(pdata->jack.jack,
SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(pdata->jack.jack,
SND_JACK_BTN_3, KEY_VOLUMEDOWN);
err = snd_soc_component_set_jack(target_dai->component,
&pdata->jack, NULL);
if (err == 0)
pdata->jack_init = true;
if (!pdata->jack_init)
pr_warn("%s: fail to init hs jack %s\n", __func__,
(pdata->jack_np)?pdata->jack_np->name:"");
}
static void init_backend_control(struct snd_soc_pcm_runtime *rtd, u32 id)
{
u32 idx;
struct snd_soc_dai *cpu_dai;
idx = AOC_ID_TO_INDEX(id);
if (idx >= ARRAY_SIZE(be_res_map))
return;
if (be_res_map[idx].num_controls == 0 ||
!be_res_map[idx].controls)
return;
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
snd_soc_add_dai_controls(cpu_dai,
be_res_map[idx].controls, be_res_map[idx].num_controls);
}
static int aoc_card_late_probe(struct snd_soc_card *card)
{
struct aoc_chip *chip =
(struct aoc_chip *)snd_soc_card_get_drvdata(card);
int err, i;
struct snd_soc_pcm_runtime *rtd;
struct snd_card_pdata *pdata =
container_of(chip, struct snd_card_pdata, g_chip);
u32 id;
chip->card = card->snd_card;
/*
* TODO: make the service list
* NOT have to be in the same order as pcm device list
*/
for (i = 0; i < aoc_audio_service_num() - 2; i++) {
chip->avail_substreams |= (1 << i);
}
err = snd_aoc_new_ctl(chip);
if (err < 0)
pr_err("%s: fail to new ctrl %d", __func__, err);
/* Default BE setting */
memcpy(pdata->be_params, default_be_params, sizeof(default_be_params));
/* Register HW control */
list_for_each_entry (rtd, &card->rtd_list, list) {
if (!rtd->dai_link->no_pcm)
continue;
id = rtd->dai_link->id;
if (!(id & AOC_BE))
continue;
init_headset_jack(card, rtd, id);
init_backend_control(rtd, id);
}
/* add for aocdump detect aoc SSR */
for (i = 0; i < ARRAY_SIZE(aoc_detect); i++)
init_audio_state_query(card, aoc_detect[i]);
return 0;
}
static int snd_aoc_init(struct aoc_chip *chip)
{
int i;
chip->mic_loopback_enabled = 0;
chip->default_mic_id = DEFAULT_MICPHONE_ID;
chip->buildin_mic_id_list[0] = DEFAULT_MICPHONE_ID;
for (i = 1; i < NUM_OF_BUILTIN_MIC; i++) {
chip->buildin_mic_id_list[i] = -1;
}
chip->default_sink_id = DEFAULT_AUDIO_SINK_ID;
chip->sink_id_list[0] = DEFAULT_AUDIO_SINK_ID;
for (i = 1; i < ARRAY_SIZE(chip->sink_id_list); i++) {
chip->sink_id_list[i] = -1;
}
chip->audio_capture_mic_source = BUILTIN_MIC;
chip->voice_call_mic_source = 0;
chip->voice_call_mic_mute = 0;
chip->compr_offload_volume = 15;
chip->voice_call_audio_enable = 1;
chip->mic_spatial_module_enable = 0;
chip->sidetone_enable = 0;
chip->voip_rx_prepared = 0;
chip->voip_tx_prepared = 0;
chip->telephony_curr_mic = NULL_PATH;
chip->telephony_curr_sink = NULL_PATH;
chip->telephony_expect_mic = NULL_PATH;
chip->telephony_expect_sink = NULL_PATH;
chip->pcm_wait_time_in_ms = DEFAULT_PCM_WAIT_TIME_IN_MSECS;
chip->voice_pcm_wait_time_in_ms = DEFAULT_VOICE_PCM_WAIT_TIME_IN_MSECS;
/* Default values for playback volume and mute */
chip->volume = 1000;
chip->mute = 1;
mutex_init(&chip->audio_mutex);
mutex_init(&chip->audio_cmd_chan_mutex);
spin_lock_init(&chip->audio_lock);
return 0;
}
static int aoc_snd_card_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct snd_soc_card *card;
int ret;
struct aoc_service_dev *aoc_dev;
struct snd_card_pdata *pdata;
pr_info("%s", __func__);
if (!np)
return -ENOSYS;
/* Check if the AoC service is up */
ret = alloc_aoc_audio_service(CMD_OUTPUT_CHANNEL, &aoc_dev, NULL, NULL);
if (ret < 0) {
if (ret == -EPROBE_DEFER)
pr_info("%s: wait for aoc output ctrl\n", __func__);
else
pr_err("%s: Failed to get aoc output ctrl %d\n",
__func__, ret);
goto err;
} else {
free_aoc_audio_service(CMD_OUTPUT_CHANNEL, aoc_dev);
}
/* Allocate the private data and the DAI link array */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
pr_err("%s: fail to allocate mem", __func__);
return -ENOMEM;
}
/* Allocate the private data */
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
pr_err("%s: fail to allocate mem for pdata", __func__);
return -ENOMEM;
}
ret = snd_aoc_init(&pdata->g_chip);
if (ret < 0) {
pr_err("%s: Failed to init aoc chip\n", __func__);
goto err;
}
pdata->g_chip.wakelock = wakeup_source_register(dev, dev_name(dev));
card->owner = THIS_MODULE;
card->dev = dev;
card->late_probe = aoc_card_late_probe;
mutex_init(&pdata->mutex);
ret = aoc_snd_card_parse_of(np, card, pdata);
if (ret) {
goto err;
}
snd_soc_card_set_drvdata(card, &pdata->g_chip);
ret = snd_soc_register_card(card);
if (ret < 0) {
if (ret == -EPROBE_DEFER) {
pr_info("%s: defer the probe %d", __func__, ret);
} else
pr_info("%s: snd register fail %d", __func__, ret);
goto err;
}
return 0;
err:
return ret;
}
static int aoc_snd_card_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
if (card) {
snd_soc_unregister_card(card);
snd_soc_card_set_drvdata(card, NULL);
}
return 0;
}
static const struct of_device_id aoc_snd_of_match[] = {
{
.compatible = "google-aoc-snd-card",
},
{},
};
MODULE_DEVICE_TABLE(of, aoc_snd_of_match);
static struct platform_driver aoc_snd_card_drv = {
.driver = {
.name = "google-aoc-snd-card",
.of_match_table = aoc_snd_of_match,
},
.probe = aoc_snd_card_probe,
.remove = aoc_snd_card_remove,
};
static int aoc_card_init(void)
{
int ret = 0, i;
pr_info("%s", __func__);
for (i = 0; i < ARRAY_SIZE(sr_map); i++)
sr_text[i] = sr_map[i].str;
for (i = 0; i < ARRAY_SIZE(fmt_map); i++)
fmt_text[i] = fmt_map[i].str;
for (i = 0; i < ARRAY_SIZE(ch_map); i++)
ch_text[i] = ch_map[i].str;
ret = platform_driver_register(&aoc_snd_card_drv);
if (ret) {
pr_err("error registering aoc pcm drv %d .\n", ret);
goto exit;
}
exit:
return ret;
}
static void aoc_card_exit(void)
{
platform_driver_unregister(&aoc_snd_card_drv);
}
module_init(aoc_card_init);
module_exit(aoc_card_exit);
MODULE_AUTHOR("google aoc team");
MODULE_DESCRIPTION("Alsa driver for aoc sound card");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:aoc_alsa_card");