// SPDX-License-Identifier: GPL-2.0
//
// cs35l41.c -- CS35l41 ALSA SoC audio driver
//
// Copyright 2017-2021 Cirrus Logic, Inc.
//
// Author: David Rhodes <david.rhodes@cirrus.com>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "cs35l41.h"
static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = {
"VA",
"VP",
};
struct cs35l41_pll_sysclk_config {
int freq;
int clk_cfg;
};
static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = {
{ 32768, 0x00 },
{ 8000, 0x01 },
{ 11025, 0x02 },
{ 12000, 0x03 },
{ 16000, 0x04 },
{ 22050, 0x05 },
{ 24000, 0x06 },
{ 32000, 0x07 },
{ 44100, 0x08 },
{ 48000, 0x09 },
{ 88200, 0x0A },
{ 96000, 0x0B },
{ 128000, 0x0C },
{ 176400, 0x0D },
{ 192000, 0x0E },
{ 256000, 0x0F },
{ 352800, 0x10 },
{ 384000, 0x11 },
{ 512000, 0x12 },
{ 705600, 0x13 },
{ 750000, 0x14 },
{ 768000, 0x15 },
{ 1000000, 0x16 },
{ 1024000, 0x17 },
{ 1200000, 0x18 },
{ 1411200, 0x19 },
{ 1500000, 0x1A },
{ 1536000, 0x1B },
{ 2000000, 0x1C },
{ 2048000, 0x1D },
{ 2400000, 0x1E },
{ 2822400, 0x1F },
{ 3000000, 0x20 },
{ 3072000, 0x21 },
{ 3200000, 0x22 },
{ 4000000, 0x23 },
{ 4096000, 0x24 },
{ 4800000, 0x25 },
{ 5644800, 0x26 },
{ 6000000, 0x27 },
{ 6144000, 0x28 },
{ 6250000, 0x29 },
{ 6400000, 0x2A },
{ 6500000, 0x2B },
{ 6750000, 0x2C },
{ 7526400, 0x2D },
{ 8000000, 0x2E },
{ 8192000, 0x2F },
{ 9600000, 0x30 },
{ 11289600, 0x31 },
{ 12000000, 0x32 },
{ 12288000, 0x33 },
{ 12500000, 0x34 },
{ 12800000, 0x35 },
{ 13000000, 0x36 },
{ 13500000, 0x37 },
{ 19200000, 0x38 },
{ 22579200, 0x39 },
{ 24000000, 0x3A },
{ 24576000, 0x3B },
{ 25000000, 0x3C },
{ 25600000, 0x3D },
{ 26000000, 0x3E },
{ 27000000, 0x3F },
};
struct cs35l41_fs_mon_config {
int freq;
unsigned int fs1;
unsigned int fs2;
};
static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
{ 32768, 2254, 3754 },
{ 8000, 9220, 15364 },
{ 11025, 6148, 10244 },
{ 12000, 6148, 10244 },
{ 16000, 4612, 7684 },
{ 22050, 3076, 5124 },
{ 24000, 3076, 5124 },
{ 32000, 2308, 3844 },
{ 44100, 1540, 2564 },
{ 48000, 1540, 2564 },
{ 88200, 772, 1284 },
{ 96000, 772, 1284 },
{ 128000, 580, 964 },
{ 176400, 388, 644 },
{ 192000, 388, 644 },
{ 256000, 292, 484 },
{ 352800, 196, 324 },
{ 384000, 196, 324 },
{ 512000, 148, 244 },
{ 705600, 100, 164 },
{ 750000, 100, 164 },
{ 768000, 100, 164 },
{ 1000000, 76, 124 },
{ 1024000, 76, 124 },
{ 1200000, 64, 104 },
{ 1411200, 52, 84 },
{ 1500000, 52, 84 },
{ 1536000, 52, 84 },
{ 2000000, 40, 64 },
{ 2048000, 40, 64 },
{ 2400000, 34, 54 },
{ 2822400, 28, 44 },
{ 3000000, 28, 44 },
{ 3072000, 28, 44 },
{ 3200000, 27, 42 },
{ 4000000, 22, 34 },
{ 4096000, 22, 34 },
{ 4800000, 19, 29 },
{ 5644800, 16, 24 },
{ 6000000, 16, 24 },
{ 6144000, 16, 24 },
};
static int cs35l41_get_fs_mon_config_index(int freq)
{
int i;
for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) {
if (cs35l41_fs_mon[i].freq == freq)
return i;
}
return -EINVAL;
}
static const DECLARE_TLV_DB_RANGE(dig_vol_tlv,
0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200));
static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
static const struct snd_kcontrol_new dre_ctrl =
SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
static const char * const cs35l41_pcm_sftramp_text[] = {
"Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"
};
static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
CS35L41_AMP_DIG_VOL_CTRL, 0,
cs35l41_pcm_sftramp_text);
static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (cs35l41->dsp.cs_dsp.booted)
return 0;
return wm_adsp_early_event(w, kcontrol, event);
case SND_SOC_DAPM_PRE_PMD:
if (cs35l41->dsp.preloaded)
return 0;
if (cs35l41->dsp.cs_dsp.running) {
ret = wm_adsp_event(w, kcontrol, event);
if (ret)
return ret;
}
return wm_adsp_early_event(w, kcontrol, event);
default:
return 0;
}
}
static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
enum cs35l41_cspl_mbox_status sts)
{
switch (cmd) {
case CSPL_MBOX_CMD_NONE:
case CSPL_MBOX_CMD_UNKNOWN_CMD:
return true;
case CSPL_MBOX_CMD_PAUSE:
case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
return (sts == CSPL_MBOX_STS_PAUSED);
case CSPL_MBOX_CMD_RESUME:
return (sts == CSPL_MBOX_STS_RUNNING);
case CSPL_MBOX_C
|