971 lines
25 KiB
C
971 lines
25 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Rockchip Audio Delta-sigma Digital Converter Interface
|
|
*
|
|
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/reset.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
#include <sound/tlv.h>
|
|
#include "rk_dsm.h"
|
|
|
|
#define RK3506_DSM_AUDM0_EN BIT(0)
|
|
#define RK3506_DSM_AUDM1_EN BIT(1)
|
|
#define RK3506_IOC1_REG_BASE (0xff660000)
|
|
#define RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0 (0x0030) /* DSM_AUD_RP_M0 / DSM_AUD_RN_M0 */
|
|
#define RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK GENMASK(11, 8)
|
|
#define RK3506_GPIO1_IOC_DSM_AUD_RP_M0 (4 << 8)
|
|
#define RK3506_GPIO1_IOC_GPIO2C2 (0 << 8)
|
|
#define RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK GENMASK(7, 4)
|
|
#define RK3506_GPIO1_IOC_DSM_AUD_RN_M0 (4 << 4)
|
|
#define RK3506_GPIO1_IOC_GPIO2C1 (0 << 4)
|
|
#define RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0 (0x0038) /* DSM_AUD_LP_M0 / DSM_AUD_LN_M0 */
|
|
#define RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK GENMASK(7, 4)
|
|
#define RK3506_GPIO1_IOC_DSM_AUD_LP_M0 (4 << 4)
|
|
#define RK3506_GPIO1_IOC_GPIO2D1 (0 << 4)
|
|
#define RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK GENMASK(3, 0)
|
|
#define RK3506_GPIO1_IOC_DSM_AUD_LN_M0 (4 << 0)
|
|
#define RK3506_GPIO1_IOC_GPIO2D0 (0 << 0)
|
|
#define RK3506_IOC2_REG_BASE (0xff4d8000)
|
|
#define RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1 (0x004c) /* DSM_AUD_LP_M1 / DSM_AUD_LN_M1 / DSM_AUD_RP_M1 / DSM_AUD_RN_M1 */
|
|
#define RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK GENMASK(15, 12)
|
|
#define RK3506_GPIO2_IOC_DSM_AUD_LP_M1 (2 << 12)
|
|
#define RK3506_GPIO2_IOC_GPIO2B7 (0 << 12)
|
|
#define RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK GENMASK(11, 8)
|
|
#define RK3506_GPIO2_IOC_DSM_AUD_LN_M1 (2 << 8)
|
|
#define RK3506_GPIO2_IOC_GPIO2B6 (0 << 8)
|
|
#define RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK GENMASK(7, 4)
|
|
#define RK3506_GPIO2_IOC_DSM_AUD_RP_M1 (2 << 4)
|
|
#define RK3506_GPIO2_IOC_GPIO2B5 (0 << 4)
|
|
#define RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK GENMASK(3, 0)
|
|
#define RK3506_GPIO2_IOC_DSM_AUD_RN_M1 (2 << 0)
|
|
#define RK3506_GPIO2_IOC_GPIO2B4 (0 << 0)
|
|
|
|
#define RK3506_GRF_SOC_CON0 (0x0)
|
|
#define RK3506_DSM_SEL (9)
|
|
#define RK3562_GRF_PERI_AUDIO_CON (0x0070)
|
|
#define RK3576_SYS_GRF_SOC_CON2 (0x0008)
|
|
#define RK3576_DSM_SEL (0x0)
|
|
|
|
#define RKDSM_VOL_VAL_MAX (0xff)
|
|
|
|
enum {
|
|
RKDSM_ON_GPIO = 0,
|
|
RKDSM_ON_FUNC,
|
|
};
|
|
|
|
struct rk_dsm_soc_data {
|
|
int (*init)(struct device *dev);
|
|
void (*deinit)(struct device *dev);
|
|
int (*iomux_switch)(struct device *dev, int type);
|
|
};
|
|
|
|
struct rk_dsm_vols {
|
|
int vol_l;
|
|
int vol_r;
|
|
int polarity;
|
|
};
|
|
|
|
struct rk_dsm_iomux_res {
|
|
phys_addr_t regbase;
|
|
unsigned long size;
|
|
};
|
|
|
|
struct rk_dsm_iomux {
|
|
void __iomem **ioc;
|
|
unsigned int audm_en;
|
|
int res_num;
|
|
};
|
|
|
|
struct rk_dsm_priv {
|
|
struct regmap *grf;
|
|
struct regmap *regmap;
|
|
struct clk *clk_dac;
|
|
struct clk *pclk;
|
|
unsigned int pa_ctl_delay_ms;
|
|
struct gpio_desc *pa_ctl;
|
|
struct reset_control *rc;
|
|
const struct rk_dsm_soc_data *data;
|
|
struct device *dev;
|
|
struct rk_dsm_vols vols;
|
|
struct rk_dsm_iomux iomuxes;
|
|
struct pinctrl *pinctrl;
|
|
struct pinctrl_state *pin_state;
|
|
struct pinctrl_state *io_state;
|
|
};
|
|
|
|
/* DAC digital gain */
|
|
static const DECLARE_TLV_DB_SCALE(dac_tlv, -95625, 375, 0);
|
|
|
|
/* DAC Cutoff for High Pass Filter */
|
|
static const char * const dac_hpf_cutoff_text[] = {
|
|
"80Hz", "100Hz", "120Hz", "140Hz",
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(dac_hpf_cutoff_enum, DACHPF, 4,
|
|
dac_hpf_cutoff_text);
|
|
|
|
static const char * const pa_ctl[] = {"Off", "On"};
|
|
|
|
static const struct soc_enum pa_enum =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(pa_ctl), pa_ctl);
|
|
|
|
static int rk_dsm_dac_vol_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
unsigned int val = snd_soc_component_read(component, mc->reg);
|
|
unsigned int sign = snd_soc_component_read(component, DACVOGP);
|
|
unsigned int mask = (1 << fls(mc->max)) - 1;
|
|
unsigned int shift = mc->shift;
|
|
int mid = mc->max / 2;
|
|
int uv;
|
|
|
|
uv = (val >> shift) & mask;
|
|
if (sign)
|
|
uv = mid + uv;
|
|
else
|
|
uv = mid - uv;
|
|
|
|
ucontrol->value.integer.value[0] = uv;
|
|
ucontrol->value.integer.value[1] = uv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_dac_vol_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(component);
|
|
unsigned int reg = mc->reg;
|
|
unsigned int rreg = mc->rreg;
|
|
unsigned int shift = mc->shift;
|
|
unsigned int mask = (1 << fls(mc->max)) - 1;
|
|
unsigned int val, val_mask, sign;
|
|
int uv = ucontrol->value.integer.value[0];
|
|
int min = mc->min;
|
|
int mid = mc->max / 2;
|
|
|
|
if (uv > mid) {
|
|
sign = DSM_DACVOGP_VOLGPL0_POS | DSM_DACVOGP_VOLGPR0_POS;
|
|
uv = uv - mid;
|
|
} else {
|
|
sign = DSM_DACVOGP_VOLGPL0_NEG | DSM_DACVOGP_VOLGPR0_NEG;
|
|
uv = mid - uv;
|
|
}
|
|
|
|
val = ((uv + min) & mask);
|
|
val_mask = mask << shift;
|
|
val = val << shift;
|
|
|
|
snd_soc_component_update_bits(component, reg, val_mask, val);
|
|
snd_soc_component_update_bits(component, rreg, val_mask, val);
|
|
snd_soc_component_write(component, DACVOGP, sign);
|
|
rd->vols.vol_l = val;
|
|
rd->vols.vol_r = val;
|
|
rd->vols.polarity = sign;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_dac_pa_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
struct rk_dsm_priv *rd = snd_soc_component_get_drvdata(component);
|
|
|
|
if (!rd->pa_ctl)
|
|
return -EINVAL;
|
|
|
|
ucontrol->value.enumerated.item[0] = gpiod_get_value(rd->pa_ctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_dac_pa_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
struct rk_dsm_priv *rd = snd_soc_component_get_drvdata(component);
|
|
|
|
if (!rd->pa_ctl)
|
|
return -EINVAL;
|
|
|
|
gpiod_set_value(rd->pa_ctl, ucontrol->value.enumerated.item[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new rk_dsm_snd_controls[] = {
|
|
SOC_DOUBLE_R_EXT_TLV("DAC Digital Volume",
|
|
DACVOLL0, DACVOLR0, 0, 0x1fe, 0,
|
|
rk_dsm_dac_vol_get,
|
|
rk_dsm_dac_vol_put,
|
|
dac_tlv),
|
|
|
|
SOC_ENUM("DAC HPF Cutoff", dac_hpf_cutoff_enum),
|
|
SOC_SINGLE("DAC HPF Switch", DACHPF, 0, 1, 0),
|
|
SOC_ENUM_EXT("Power Amplifier", pa_enum,
|
|
rk_dsm_dac_pa_get,
|
|
rk_dsm_dac_pa_put),
|
|
};
|
|
|
|
/*
|
|
* ACDC_CLK D2A_CLK D2A_SYNC Sample rates supported
|
|
* 49.152MHz 49.152MHz 6.144MHz 12/24/48/96/192kHz
|
|
* 45.154MHz 45.154MHz 5.644MHz 11.025/22.05/44.1/88.2/176.4kHz
|
|
* 32.768MHz 32.768MHz 4.096MHz 8/16/32/64/128kHz
|
|
*/
|
|
static void rk_dsm_get_clk(unsigned int samplerate,
|
|
unsigned int *mclk,
|
|
unsigned int *sclk)
|
|
{
|
|
switch (samplerate) {
|
|
case 12000:
|
|
case 24000:
|
|
case 48000:
|
|
case 96000:
|
|
case 192000:
|
|
*mclk = 49152000;
|
|
*sclk = 6144000;
|
|
break;
|
|
case 11025:
|
|
case 22050:
|
|
case 44100:
|
|
case 88200:
|
|
case 176400:
|
|
*mclk = 45158400;
|
|
*sclk = 5644800;
|
|
break;
|
|
case 8000:
|
|
case 16000:
|
|
case 32000:
|
|
case 64000:
|
|
case 128000:
|
|
*mclk = 32768000;
|
|
*sclk = 4096000;
|
|
break;
|
|
default:
|
|
*mclk = 0;
|
|
*sclk = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rk_dsm_enable_clk_dac(struct rk_dsm_priv *rd)
|
|
{
|
|
regmap_update_bits(rd->regmap, DACCLKCTRL,
|
|
DSM_DACCLKCTRL_DAC_CKE_MASK |
|
|
DSM_DACCLKCTRL_I2SRX_CKE_MASK |
|
|
DSM_DACCLKCTRL_CKE_BCLKRX_MASK |
|
|
DSM_DACCLKCTRL_DAC_SYNC_ENA_MASK |
|
|
DSM_DACCLKCTRL_DAC_MODE_ATTENU_MASK,
|
|
DSM_DACCLKCTRL_DAC_CKE_EN |
|
|
DSM_DACCLKCTRL_I2SRX_CKE_EN |
|
|
DSM_DACCLKCTRL_CKE_BCLKRX_EN |
|
|
DSM_DACCLKCTRL_DAC_SYNC_ENA_EN |
|
|
DSM_DACCLKCTRL_DAC_MODE_ATTENU_EN);
|
|
}
|
|
|
|
static int rk_dsm_set_clk(struct rk_dsm_priv *rd,
|
|
struct snd_pcm_substream *substream,
|
|
unsigned int samplerate)
|
|
{
|
|
unsigned int mclk, sclk, bclk;
|
|
unsigned int div_bclk;
|
|
|
|
rk_dsm_get_clk(samplerate, &mclk, &sclk);
|
|
if (!mclk || !sclk)
|
|
return -EINVAL;
|
|
|
|
bclk = 64 * samplerate;
|
|
div_bclk = DIV_ROUND_CLOSEST(mclk, bclk);
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
clk_set_rate(rd->clk_dac, mclk);
|
|
|
|
rk_dsm_enable_clk_dac(rd);
|
|
|
|
regmap_update_bits(rd->regmap, DACSCLKRXINT_DIV,
|
|
DSM_DACSCLKRXINT_DIV_SCKRXDIV_MASK,
|
|
DSM_DACSCLKRXINT_DIV_SCKRXDIV(div_bclk));
|
|
regmap_update_bits(rd->regmap, I2S_CKR0,
|
|
DSM_I2S_CKR0_RSD_MASK,
|
|
DSM_I2S_CKR0_RSD_64);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_set_dai_fmt(struct snd_soc_dai *dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(dai->component);
|
|
unsigned int mask = 0, val = 0;
|
|
|
|
/* master mode only */
|
|
regmap_update_bits(rd->regmap, I2S_CKR1,
|
|
DSM_I2S_CKR1_MSS_MASK,
|
|
DSM_I2S_CKR1_MSS_MASTER);
|
|
|
|
mask = DSM_I2S_CKR1_CKP_MASK |
|
|
DSM_I2S_CKR1_RLP_MASK;
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
val = DSM_I2S_CKR1_CKP_NORMAL |
|
|
DSM_I2S_CKR1_RLP_NORMAL;
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_IF:
|
|
val = DSM_I2S_CKR1_CKP_INVERTED |
|
|
DSM_I2S_CKR1_RLP_INVERTED;
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
val = DSM_I2S_CKR1_CKP_INVERTED |
|
|
DSM_I2S_CKR1_RLP_NORMAL;
|
|
break;
|
|
case SND_SOC_DAIFMT_NB_IF:
|
|
val = DSM_I2S_CKR1_CKP_NORMAL |
|
|
DSM_I2S_CKR1_RLP_INVERTED;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(rd->regmap, I2S_CKR1, mask, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(dai->component);
|
|
unsigned int srt = 0, val = 0;
|
|
|
|
rk_dsm_set_clk(rd, substream, params_rate(params));
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
switch (params_rate(params)) {
|
|
case 8000:
|
|
case 11025:
|
|
case 12000:
|
|
srt = 0;
|
|
break;
|
|
case 16000:
|
|
case 22050:
|
|
case 24000:
|
|
srt = 1;
|
|
break;
|
|
case 32000:
|
|
case 44100:
|
|
case 48000:
|
|
srt = 2;
|
|
break;
|
|
case 64000:
|
|
case 88200:
|
|
case 96000:
|
|
srt = 3;
|
|
break;
|
|
case 128000:
|
|
case 176400:
|
|
case 192000:
|
|
srt = 4;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(rd->regmap, DACCFG1,
|
|
DSM_DACCFG1_DACSRT_MASK,
|
|
DSM_DACCFG1_DACSRT(srt));
|
|
|
|
switch (params_format(params)) {
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
val = 16;
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S24_LE:
|
|
case SNDRV_PCM_FORMAT_S32_LE:
|
|
val = 24;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(rd->regmap, I2S_RXCR0,
|
|
DSM_I2S_RXCR0_VDW_MASK,
|
|
DSM_I2S_RXCR0_VDW(val));
|
|
regmap_update_bits(rd->regmap, DACDSM_CTRL,
|
|
DSM_DACDSM_CTRL_DSM_MODE_CKE_MASK |
|
|
DSM_DACDSM_CTRL_DSM_EN_MASK,
|
|
DSM_DACDSM_CTRL_DSM_MODE_CKE_EN |
|
|
DSM_DACDSM_CTRL_DSM_EN);
|
|
|
|
regmap_update_bits(rd->regmap, I2S_XFER,
|
|
DSM_I2S_XFER_RXS_MASK,
|
|
DSM_I2S_XFER_RXS_START);
|
|
regmap_update_bits(rd->regmap, DACDIGEN,
|
|
DSM_DACDIGEN_DAC_GLBEN_MASK |
|
|
DSM_DACDIGEN_DACEN_L0R1_MASK,
|
|
DSM_DACDIGEN_DAC_GLBEN_EN |
|
|
DSM_DACDIGEN_DACEN_L0R1_EN);
|
|
|
|
if (rd->data && rd->data->iomux_switch)
|
|
rd->data->iomux_switch(rd->dev, RKDSM_ON_FUNC);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_dsm_pcm_startup(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(dai->component);
|
|
|
|
/* Recover DAC Volumes */
|
|
regmap_write(rd->regmap, DACVOLL0, rd->vols.vol_l);
|
|
regmap_write(rd->regmap, DACVOLR0, rd->vols.vol_r);
|
|
regmap_write(rd->regmap, DACVOGP, rd->vols.polarity);
|
|
|
|
gpiod_set_value(rd->pa_ctl, 1);
|
|
if (rd->pa_ctl_delay_ms)
|
|
msleep(rd->pa_ctl_delay_ms);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rk_dsm_reset(struct rk_dsm_priv *rd)
|
|
{
|
|
if (IS_ERR(rd->rc))
|
|
return;
|
|
|
|
reset_control_assert(rd->rc);
|
|
udelay(5);
|
|
reset_control_deassert(rd->rc);
|
|
udelay(5);
|
|
}
|
|
|
|
static void rk_dsm_pcm_shutdown(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(dai->component);
|
|
|
|
gpiod_set_value(rd->pa_ctl, 0);
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
regmap_update_bits(rd->regmap, DACDSM_CTRL,
|
|
DSM_DACDSM_CTRL_DSM_MODE_CKE_MASK |
|
|
DSM_DACDSM_CTRL_DSM_EN_MASK,
|
|
DSM_DACDSM_CTRL_DSM_MODE_CKE_DIS |
|
|
DSM_DACDSM_CTRL_DSM_DIS);
|
|
regmap_update_bits(rd->regmap, I2S_XFER,
|
|
DSM_I2S_XFER_RXS_MASK,
|
|
DSM_I2S_XFER_RXS_STOP);
|
|
regmap_update_bits(rd->regmap, I2S_CLR,
|
|
DSM_I2S_CLR_RXC_MASK,
|
|
DSM_I2S_CLR_RXC_CLR);
|
|
regmap_update_bits(rd->regmap, DACDIGEN,
|
|
DSM_DACDIGEN_DAC_GLBEN_MASK |
|
|
DSM_DACDIGEN_DACEN_L0R1_MASK,
|
|
DSM_DACDIGEN_DAC_GLBEN_DIS |
|
|
DSM_DACDIGEN_DACEN_L0R1_DIS);
|
|
}
|
|
|
|
rk_dsm_reset(rd);
|
|
}
|
|
|
|
static int rk_dsm_pcm_trigger(struct snd_pcm_substream *substream,
|
|
int cmd, struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_dsm_priv *rd =
|
|
snd_soc_component_get_drvdata(dai->component);
|
|
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
/**
|
|
* After receiving the stop action, adjust the volume to the
|
|
* minimum as soon as possible and switch the differential pair
|
|
* of DSM to IO state, so that the same direction change of P/N
|
|
* level can reduce the pop sound to the minimum.
|
|
*/
|
|
regmap_write(rd->regmap, DACVOLL0, RKDSM_VOL_VAL_MAX);
|
|
regmap_write(rd->regmap, DACVOLR0, RKDSM_VOL_VAL_MAX);
|
|
regmap_write(rd->regmap, DACVOGP, DSM_DACVOGP_VOLGPL0_NEG | DSM_DACVOGP_VOLGPR0_NEG);
|
|
regcache_cache_only(rd->regmap, false);
|
|
regcache_mark_dirty(rd->regmap);
|
|
regcache_sync(rd->regmap);
|
|
if (rd->data && rd->data->iomux_switch)
|
|
rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops rd_dai_ops = {
|
|
.hw_params = rk_dsm_hw_params,
|
|
.set_fmt = rk_dsm_set_dai_fmt,
|
|
.startup = rk_dsm_pcm_startup,
|
|
.shutdown = rk_dsm_pcm_shutdown,
|
|
.trigger = rk_dsm_pcm_trigger,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver rd_dai[] = {
|
|
{
|
|
.name = "rk_dsm",
|
|
.id = 0,
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = SNDRV_PCM_RATE_8000_192000,
|
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
|
SNDRV_PCM_FMTBIT_S24_LE |
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
},
|
|
.ops = &rd_dai_ops,
|
|
},
|
|
};
|
|
|
|
static const struct snd_soc_component_driver soc_codec_dev_rd = {
|
|
.controls = rk_dsm_snd_controls,
|
|
.num_controls = ARRAY_SIZE(rk_dsm_snd_controls),
|
|
};
|
|
|
|
static const struct reg_default rd_reg_defaults[] = {
|
|
{ DACVUCTL, 0x1 },
|
|
{ DACINT_DIV, 0x7 },
|
|
{ DACDSM_DIV, 0x3 },
|
|
};
|
|
|
|
static const struct regmap_config rd_regmap_config = {
|
|
.reg_bits = 32,
|
|
.reg_stride = 4,
|
|
.val_bits = 32,
|
|
.max_register = VERSION,
|
|
.reg_defaults = rd_reg_defaults,
|
|
.num_reg_defaults = ARRAY_SIZE(rd_reg_defaults),
|
|
.cache_type = REGCACHE_FLAT,
|
|
};
|
|
|
|
static int rk3506_soc_init(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
struct rk_dsm_iomux_res res[] = {
|
|
{ .regbase = RK3506_IOC1_REG_BASE, .size = 0x100, },
|
|
{ .regbase = RK3506_IOC2_REG_BASE, .size = 0x100, },
|
|
};
|
|
int c;
|
|
|
|
rd->iomuxes.res_num = ARRAY_SIZE(res);
|
|
rd->iomuxes.ioc = devm_kcalloc(dev, rd->iomuxes.res_num, sizeof(void __iomem *), GFP_KERNEL);
|
|
if (!rd->iomuxes.ioc)
|
|
return -ENOMEM;
|
|
|
|
for (c = 0; c < rd->iomuxes.res_num; c++) {
|
|
rd->iomuxes.ioc[c] = ioremap(res[c].regbase, res[c].size);
|
|
if (!rd->iomuxes.ioc[c]) {
|
|
int n;
|
|
|
|
/* iounmap previous mapped address */
|
|
for (n = 0; n < rd->iomuxes.res_num; n++) {
|
|
if (rd->iomuxes.ioc[n]) {
|
|
iounmap(rd->iomuxes.ioc[n]);
|
|
rd->iomuxes.ioc[n] = NULL;
|
|
}
|
|
}
|
|
dev_err(dev, "ioremap res[%d] start: 0x%lx failed\n",
|
|
c, (unsigned long)res[c].regbase);
|
|
return -ENOMEM;
|
|
}
|
|
dev_info(dev, "ioremap ioc res[%d] start: 0x%lx success\n", c, (unsigned long)res[c].regbase);
|
|
}
|
|
|
|
rd->pinctrl = devm_pinctrl_get(dev);
|
|
if (!IS_ERR_OR_NULL(rd->pinctrl)) {
|
|
const char *io_name;
|
|
const char *pin_name;
|
|
|
|
if (rd->iomuxes.audm_en == (RK3506_DSM_AUDM0_EN | RK3506_DSM_AUDM1_EN)) {
|
|
io_name = "audm0m1-iodown";
|
|
pin_name = "audm0m1-pins";
|
|
} else if (rd->iomuxes.audm_en == RK3506_DSM_AUDM1_EN) {
|
|
io_name = "audm1-iodown";
|
|
pin_name = "audm1-pins";
|
|
} else {
|
|
/* default case */
|
|
io_name = "audm0-iodown";
|
|
pin_name = "audm0-pins";
|
|
rd->iomuxes.audm_en = RK3506_DSM_AUDM0_EN;
|
|
}
|
|
|
|
rd->io_state = pinctrl_lookup_state(rd->pinctrl, io_name);
|
|
if (IS_ERR(rd->io_state)) {
|
|
rd->io_state = NULL;
|
|
dev_err(dev, "Have no dsm pinctrl io state by: %s\n", io_name);
|
|
}
|
|
|
|
rd->pin_state = pinctrl_lookup_state(rd->pinctrl, pin_name);
|
|
if (IS_ERR(rd->pin_state)) {
|
|
rd->pin_state = NULL;
|
|
dev_err(dev, "Have no dsm pinctrl pin state, by: %s\n", pin_name);
|
|
}
|
|
}
|
|
|
|
if (rd->data && rd->data->iomux_switch)
|
|
rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
|
|
|
|
dev_info(dev, "iomuxes audm_en: 0x%x\n", rd->iomuxes.audm_en);
|
|
/* enable internal codec to sai3 */
|
|
return regmap_write(rd->grf, RK3506_GRF_SOC_CON0,
|
|
BIT(RK3506_DSM_SEL) << 16 | BIT(RK3506_DSM_SEL));
|
|
}
|
|
|
|
static void rk3506_soc_deinit(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
int c;
|
|
|
|
regmap_write(rd->grf, RK3506_GRF_SOC_CON0, BIT(RK3506_DSM_SEL) << 16);
|
|
for (c = 0; c < rd->iomuxes.res_num; c++) {
|
|
if (rd->iomuxes.ioc[c]) {
|
|
iounmap(rd->iomuxes.ioc[c]);
|
|
rd->iomuxes.ioc[c] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rk3506_soc_iomux_switch(struct device *dev, int type)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
struct rk_dsm_iomux *iomuxes = &rd->iomuxes;
|
|
unsigned long flags;
|
|
|
|
local_irq_save(flags);
|
|
if (iomuxes->audm_en & RK3506_DSM_AUDM0_EN) {
|
|
/**
|
|
* DSM_AUD_RN_M0 - GPIO1_C1
|
|
* DSM_AUD_RP_M0 - GPIO1_C2
|
|
* DSM_AUD_LN_M0 - GPIO1_D0
|
|
* DSM_AUD_LP_M0 - GPIO1_D1
|
|
*/
|
|
if (type == RKDSM_ON_FUNC) {
|
|
writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
|
|
RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
|
|
(RK3506_GPIO1_IOC_DSM_AUD_RP_M0 |
|
|
RK3506_GPIO1_IOC_DSM_AUD_RN_M0),
|
|
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
|
|
writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
|
|
RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
|
|
(RK3506_GPIO1_IOC_DSM_AUD_LP_M0 |
|
|
RK3506_GPIO1_IOC_DSM_AUD_LN_M0),
|
|
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
|
|
} else {
|
|
/* RKDSM_ON_GPIO */
|
|
writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
|
|
RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
|
|
(RK3506_GPIO1_IOC_GPIO2C2 |
|
|
RK3506_GPIO1_IOC_GPIO2C1),
|
|
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
|
|
writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
|
|
RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
|
|
(RK3506_GPIO1_IOC_GPIO2D1 |
|
|
RK3506_GPIO1_IOC_GPIO2D0),
|
|
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
|
|
}
|
|
}
|
|
|
|
if (iomuxes->audm_en & RK3506_DSM_AUDM1_EN) {
|
|
/**
|
|
* DSM_AUD_RN_M1 - GPIO2_B4
|
|
* DSM_AUD_RP_M1 - GPIO2_B5
|
|
* DSM_AUD_LN_M1 - GPIO2_B6
|
|
* DSM_AUD_LP_M1 - GPIO2_B7
|
|
*/
|
|
if (type == RKDSM_ON_FUNC) {
|
|
writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
|
|
(RK3506_GPIO2_IOC_DSM_AUD_LP_M1 |
|
|
RK3506_GPIO2_IOC_DSM_AUD_LN_M1 |
|
|
RK3506_GPIO2_IOC_DSM_AUD_RP_M1 |
|
|
RK3506_GPIO2_IOC_DSM_AUD_RN_M1),
|
|
iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
|
|
} else {
|
|
/* RKDSM_ON_GPIO */
|
|
writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
|
|
RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
|
|
(RK3506_GPIO2_IOC_GPIO2B7 |
|
|
RK3506_GPIO2_IOC_GPIO2B6 |
|
|
RK3506_GPIO2_IOC_GPIO2B5 |
|
|
RK3506_GPIO2_IOC_GPIO2B4),
|
|
iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
|
|
}
|
|
}
|
|
local_irq_restore(flags);
|
|
|
|
/* Keeping sync with pinctrl framework */
|
|
if (type == RKDSM_ON_FUNC)
|
|
pinctrl_select_state(rd->pinctrl, rd->pin_state);
|
|
else
|
|
pinctrl_select_state(rd->pinctrl, rd->io_state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct rk_dsm_soc_data rk3506_data = {
|
|
.init = rk3506_soc_init,
|
|
.deinit = rk3506_soc_deinit,
|
|
.iomux_switch = rk3506_soc_iomux_switch,
|
|
};
|
|
|
|
static int rk3562_soc_init(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
|
|
/* enable internal codec to i2s1 */
|
|
return regmap_write(rd->grf, RK3562_GRF_PERI_AUDIO_CON,
|
|
(BIT(14) << 16 | BIT(14) | 0x0a100a10));
|
|
}
|
|
|
|
static void rk3562_soc_deinit(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
|
|
regmap_write(rd->grf, RK3562_GRF_PERI_AUDIO_CON, (BIT(14) << 16) | 0x0a100a10);
|
|
}
|
|
|
|
static const struct rk_dsm_soc_data rk3562_data = {
|
|
.init = rk3562_soc_init,
|
|
.deinit = rk3562_soc_deinit,
|
|
};
|
|
|
|
static int rk3576_soc_init(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
|
|
/* enable internal codec to sai4 */
|
|
return regmap_write(rd->grf, RK3576_SYS_GRF_SOC_CON2,
|
|
BIT(RK3576_DSM_SEL) << 16 | BIT(RK3576_DSM_SEL));
|
|
}
|
|
|
|
static void rk3576_soc_deinit(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
|
|
regmap_write(rd->grf, RK3576_SYS_GRF_SOC_CON2, BIT(RK3576_DSM_SEL) << 16);
|
|
}
|
|
|
|
static const struct rk_dsm_soc_data rk3576_data = {
|
|
.init = rk3576_soc_init,
|
|
.deinit = rk3576_soc_deinit,
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id rd_of_match[] = {
|
|
{ .compatible = "rockchip,rk3506-dsm", .data = &rk3506_data },
|
|
{ .compatible = "rockchip,rk3562-dsm", .data = &rk3562_data },
|
|
{ .compatible = "rockchip,rk3576-dsm", .data = &rk3576_data },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, rd_of_match);
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM
|
|
static int rk_dsm_runtime_resume(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
ret = clk_prepare_enable(rd->pclk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
regcache_cache_only(rd->regmap, false);
|
|
regcache_mark_dirty(rd->regmap);
|
|
ret = regcache_sync(rd->regmap);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = clk_prepare_enable(rd->clk_dac);
|
|
if (ret)
|
|
goto err;
|
|
|
|
return 0;
|
|
err:
|
|
clk_disable_unprepare(rd->pclk);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rk_dsm_runtime_suspend(struct device *dev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
|
|
|
regcache_cache_only(rd->regmap, true);
|
|
clk_disable_unprepare(rd->clk_dac);
|
|
clk_disable_unprepare(rd->pclk);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int rk_dsm_platform_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct rk_dsm_priv *rd;
|
|
void __iomem *base;
|
|
int ret = 0;
|
|
|
|
rd = devm_kzalloc(&pdev->dev, sizeof(*rd), GFP_KERNEL);
|
|
if (!rd)
|
|
return -ENOMEM;
|
|
|
|
rd->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
|
if (IS_ERR(rd->grf))
|
|
return PTR_ERR(rd->grf);
|
|
|
|
if (device_property_read_u32(&pdev->dev, "rockchip,pa-ctl-delay-ms",
|
|
&rd->pa_ctl_delay_ms))
|
|
rd->pa_ctl_delay_ms = 0;
|
|
|
|
if (device_property_read_u32(&pdev->dev, "rockchip,dsm-audm-en", &rd->iomuxes.audm_en))
|
|
rd->iomuxes.audm_en = 0;
|
|
|
|
rd->rc = devm_reset_control_get(&pdev->dev, "reset");
|
|
|
|
rd->clk_dac = devm_clk_get(&pdev->dev, "dac");
|
|
if (IS_ERR(rd->clk_dac))
|
|
return PTR_ERR(rd->clk_dac);
|
|
|
|
rd->pclk = devm_clk_get(&pdev->dev, "pclk");
|
|
if (IS_ERR(rd->pclk))
|
|
return PTR_ERR(rd->pclk);
|
|
|
|
base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(base))
|
|
return PTR_ERR(base);
|
|
|
|
rd->regmap =
|
|
devm_regmap_init_mmio(&pdev->dev, base, &rd_regmap_config);
|
|
if (IS_ERR(rd->regmap))
|
|
return PTR_ERR(rd->regmap);
|
|
|
|
rd->dev = &pdev->dev;
|
|
platform_set_drvdata(pdev, rd);
|
|
|
|
rd->data = device_get_match_data(&pdev->dev);
|
|
if (rd->data && rd->data->init) {
|
|
ret = rd->data->init(&pdev->dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
if (!pm_runtime_enabled(&pdev->dev)) {
|
|
ret = rk_dsm_runtime_resume(&pdev->dev);
|
|
if (ret)
|
|
goto err_pm_disable;
|
|
}
|
|
|
|
regmap_update_bits(rd->regmap, DACDSM_CTRL,
|
|
DSM_DACDSM_CTRL_DSM_MODE_MASK,
|
|
DSM_DACDSM_CTRL_DSM_MODE_0);
|
|
|
|
rd->pa_ctl = devm_gpiod_get_optional(&pdev->dev, "pa-ctl",
|
|
GPIOD_OUT_LOW);
|
|
|
|
if (!rd->pa_ctl) {
|
|
dev_info(&pdev->dev, "no need pa-ctl gpio\n");
|
|
} else if (IS_ERR(rd->pa_ctl)) {
|
|
ret = PTR_ERR(rd->pa_ctl);
|
|
dev_err(&pdev->dev, "fail to request gpio pa-ctl\n");
|
|
goto err_suspend;
|
|
}
|
|
|
|
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rd,
|
|
rd_dai, ARRAY_SIZE(rd_dai));
|
|
|
|
if (ret)
|
|
goto err_suspend;
|
|
|
|
return 0;
|
|
|
|
err_suspend:
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
rk_dsm_runtime_suspend(&pdev->dev);
|
|
err_pm_disable:
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
if (rd->data && rd->data->deinit)
|
|
rd->data->deinit(&pdev->dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rk_dsm_platform_remove(struct platform_device *pdev)
|
|
{
|
|
struct rk_dsm_priv *rd = dev_get_drvdata(&pdev->dev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
rk_dsm_runtime_suspend(&pdev->dev);
|
|
|
|
if (rd->data && rd->data->deinit)
|
|
rd->data->deinit(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops rd_pm = {
|
|
SET_RUNTIME_PM_OPS(rk_dsm_runtime_suspend,
|
|
rk_dsm_runtime_resume, NULL)
|
|
};
|
|
|
|
static struct platform_driver rk_dsm_driver = {
|
|
.driver = {
|
|
.name = "rk_dsm",
|
|
.of_match_table = of_match_ptr(rd_of_match),
|
|
.pm = &rd_pm,
|
|
},
|
|
.probe = rk_dsm_platform_probe,
|
|
.remove = rk_dsm_platform_remove,
|
|
};
|
|
module_platform_driver(rk_dsm_driver);
|
|
|
|
MODULE_AUTHOR("Jason Zhu <jason.zhu@rock-chips.com>");
|
|
MODULE_DESCRIPTION("ASoC Rockchip Delta-sigma Digital Converter Driver");
|
|
MODULE_LICENSE("GPL");
|