Re: [PATCH 5/6] ASoC: sun4i-codec: support allwinner H616 codec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 29 Sep 2024 23:06:06 +1300
Ryan Walklin <ryan@xxxxxxxxxxxxx> wrote:

Hi,

so I have no clue about Linux' audio subsystem or ASoC, so don't dare to
review this patch. I had a look through the register list in the manual,
though, and compared the bits and offsets against the constants defined:
they match. And I can confirm that it works (TM). One small thing, though:

> The H616 SoC codec is playback-only with a single line-out route, and
> has some register differences from prior codecs.
> 
> Add the required compatible string, registers, quirks, DAPM widgets,
> codec controls and routes, based on existing devices and the H616
> datasheet.
> 
> Signed-off-by: Ryan Walklin <ryan@xxxxxxxxxxxxx>
> ---
>  sound/soc/sunxi/sun4i-codec.c | 202 ++++++++++++++++++++++++++++++++++
>  1 file changed, 202 insertions(+)
> 
> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
> index 312d2655c3f4e..1cdda20e8ed59 100644
> --- a/sound/soc/sunxi/sun4i-codec.c
> +++ b/sound/soc/sunxi/sun4i-codec.c
> @@ -226,6 +226,43 @@
>  #define SUN8I_H3_CODEC_DAC_DBG			(0x48)
>  #define SUN8I_H3_CODEC_ADC_DBG			(0x4c)
>  
> +/* H616 specific registers */
> +#define SUN50I_H616_CODEC_DAC_FIFOC		(0x10)
> +
> +#define SUN50I_DAC_FIFO_STA			(0x14)
> +#define SUN50I_DAC_TXE_INT			(3)
> +#define SUN50I_DAC_TXU_INT			(2)
> +#define SUN50I_DAC_TXO_INT			(1)
> +
> +#define SUN50I_DAC_CNT				(0x24)
> +#define SUN50I_DAC_DG_REG			(0x28)
> +#define SUN50I_DAC_DAP_CTL			(0xf0)
> +
> +#define SUN50I_H616_DAC_AC_DAC_REG		(0x310)
> +#define SUN50I_H616_DAC_LEN			(15)
> +#define SUN50I_H616_DAC_REN			(14)
> +#define SUN50I_H616_LINEOUTL_EN			(13)
> +#define SUN50I_H616_LMUTE			(12)
> +#define SUN50I_H616_LINEOUTR_EN			(11)
> +#define SUN50I_H616_RMUTE			(10)
> +#define SUN50I_H616_RSWITCH			(9)
> +#define SUN50I_H616_RAMPEN			(8)
> +#define SUN50I_H616_LINEOUTL_SEL		(6)
> +#define SUN50I_H616_LINEOUTR_SEL		(5)
> +#define SUN50I_H616_LINEOUT_VOL			(0)
> +
> +#define SUN50I_H616_DAC_AC_MIXER_REG		(0x314)
> +#define SUN50I_H616_LMIX_LDAC			(21)
> +#define SUN50I_H616_LMIX_RDAC			(20)
> +#define SUN50I_H616_RMIX_RDAC			(17)
> +#define SUN50I_H616_RMIX_LDAC			(16)
> +#define SUN50I_H616_LMIXEN			(11)
> +#define SUN50I_H616_RMIXEN			(10)
> +
> +#define SUN50I_H616_DAC_AC_RAMP_REG		(0x31c)
> +#define SUN50I_H616_RAMP_STEP			(4)
> +#define SUN50I_H616_RDEN			(0)
> +
>  /* TODO H3 DAP (Digital Audio Processing) bits */
>  
>  struct sun4i_codec {
> @@ -1520,6 +1557,149 @@ static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
>  	return card;
>  };
>  
> +static const struct snd_kcontrol_new sun50i_h616_codec_codec_controls[] = {
> +	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
> +		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
> +		       sun6i_codec_dvol_scale),
> +	SOC_SINGLE_TLV("Line Out Playback Volume",
> +		       SUN50I_H616_DAC_AC_DAC_REG,
> +		       SUN50I_H616_LINEOUT_VOL, 0x1f, 0,
> +		       sun6i_codec_lineout_vol_scale),
> +	SOC_DOUBLE("Line Out Playback Switch",
> +		   SUN50I_H616_DAC_AC_DAC_REG,
> +		   SUN50I_H616_LINEOUTL_EN,
> +		   SUN50I_H616_LINEOUTR_EN, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new sun50i_h616_codec_mixer_controls[] = {
> +	SOC_DAPM_DOUBLE("DAC Playback Switch",
> +			SUN50I_H616_DAC_AC_MIXER_REG,
> +			SUN50I_H616_LMIX_LDAC,
> +			SUN50I_H616_RMIX_RDAC, 1, 0),
> +	SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
> +			SUN50I_H616_DAC_AC_MIXER_REG,
> +			SUN50I_H616_LMIX_RDAC,
> +			SUN50I_H616_RMIX_LDAC, 1, 0),
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun50i_h616_codec_lineout_src_enum,
> +			    SUN50I_H616_DAC_AC_DAC_REG,
> +			    SUN50I_H616_LINEOUTL_SEL,
> +			    SUN50I_H616_LINEOUTR_SEL,
> +			    sun6i_codec_lineout_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun50i_h616_codec_lineout_src[] = {
> +		SOC_DAPM_ENUM("Line Out Source Playback Route",
> +			      sun50i_h616_codec_lineout_src_enum),
> +};
> +
> +static const struct snd_soc_dapm_widget sun50i_h616_codec_codec_widgets[] = {
> +	/* Digital parts of the DACs */
> +	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
> +			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
> +			    NULL, 0),
> +
> +	/* Analog parts of the DACs */
> +	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",
> +			 SUN50I_H616_DAC_AC_DAC_REG,
> +			 SUN50I_H616_DAC_LEN, 0),
> +	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
> +			 SUN50I_H616_DAC_AC_DAC_REG,
> +			 SUN50I_H616_DAC_REN, 0),
> +
> +	/* Mixers */
> +	SOC_MIXER_ARRAY("Left Mixer", SUN50I_H616_DAC_AC_MIXER_REG,
> +			SUN50I_H616_LMIXEN, 0,
> +			sun50i_h616_codec_mixer_controls),
> +	SOC_MIXER_ARRAY("Right Mixer", SUN50I_H616_DAC_AC_MIXER_REG,
> +			SUN50I_H616_RMIXEN, 0,
> +			sun50i_h616_codec_mixer_controls),
> +
> +	/* Line Out path */
> +	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
> +			 SND_SOC_NOPM, 0, 0, sun50i_h616_codec_lineout_src),
> +	SND_SOC_DAPM_OUT_DRV("Line Out Ramp Controller",
> +			     SUN50I_H616_DAC_AC_RAMP_REG,
> +			     SUN50I_H616_RDEN, 0, NULL, 0),
> +	SND_SOC_DAPM_OUTPUT("LINEOUT"),
> +};
> +
> +static const struct snd_soc_component_driver sun50i_h616_codec_codec = {
> +	.controls   = sun50i_h616_codec_codec_controls,
> +	.num_controls   = ARRAY_SIZE(sun50i_h616_codec_codec_controls),
> +	.dapm_widgets   = sun50i_h616_codec_codec_widgets,
> +	.num_dapm_widgets = ARRAY_SIZE(sun50i_h616_codec_codec_widgets),
> +	.idle_bias_on   = 1,
> +	.use_pmdown_time  = 1,
> +	.endianness   = 1,
> +};
> +
> +static const struct snd_kcontrol_new sun50i_h616_card_controls[] = {
> +	SOC_DAPM_PIN_SWITCH("LINEOUT"),
> +};
> +
> +static const struct snd_soc_dapm_widget sun50i_h616_codec_card_dapm_widgets[] = {
> +	SND_SOC_DAPM_LINE("Line Out", NULL),
> +	SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
> +};
> +
> +/* Connect digital side enables to analog side widgets */
> +static const struct snd_soc_dapm_route sun50i_h616_codec_card_routes[] = {
> +	/* DAC Routes */
> +	{ "Left DAC", NULL, "DAC Enable" },
> +	{ "Right DAC", NULL, "DAC Enable" },
> +
> +	/* Left Mixer Routes */
> +	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
> +	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
> +
> +	/* Right Mixer Routes */
> +	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
> +	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
> +
> +	/* Line Out Routes */
> +	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
> +	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
> +	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
> +	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
> +	{ "Line Out Ramp Controller", NULL, "Line Out Source Playback Route" },
> +	{ "LINEOUT", NULL, "Line Out Ramp Controller" },
> +};
> +
> +static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev)
> +{
> +	struct snd_soc_card *card;
> +	int ret;
> +
> +	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
> +	if (!card)
> +		return ERR_PTR(-ENOMEM);
> +
> +	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
> +	if (!card->dai_link)
> +		return ERR_PTR(-ENOMEM);
> +
> +	card->dai_link->playback_only = true;
> +	card->dai_link->capture_only = false;
> +
> +	card->dev		= dev;
> +	card->owner		= THIS_MODULE;
> +	card->name		= "H616 Audio Codec";

I get an error message, complaining about this name being too long:

[    3.343325] ASoC: driver name too long 'H616 Audio Codec' -> 'H616_Audio_Code'

This happens when "name" is used as a fallback for a missing "driver_name"
member. But its storage is limited to 16 characters, so we are short by
one (for the NUL byte). I could fix that by adding:

	card->driver_name	= "sun4i-codec";

Cheers,
Andre

> +	card->controls		= sun50i_h616_card_controls;
> +	card->num_controls	= ARRAY_SIZE(sun50i_h616_card_controls);
> +	card->dapm_widgets	= sun50i_h616_codec_card_dapm_widgets;
> +	card->num_dapm_widgets	= ARRAY_SIZE(sun50i_h616_codec_card_dapm_widgets);
> +	card->dapm_routes	= sun50i_h616_codec_card_routes;
> +	card->num_dapm_routes	= ARRAY_SIZE(sun50i_h616_codec_card_routes);
> +	card->fully_routed	= true;
> +
> +	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
> +	if (ret)
> +		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
> +
> +	return card;
> +};
> +
>  static const struct regmap_config sun4i_codec_regmap_config = {
>  	.reg_bits	= 32,
>  	.reg_stride	= 4,
> @@ -1562,6 +1742,14 @@ static const struct regmap_config sun8i_v3s_codec_regmap_config = {
>  	.max_register	= SUN8I_H3_CODEC_ADC_DBG,
>  };
>  
> +static const struct regmap_config sun50i_h616_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN50I_H616_DAC_AC_RAMP_REG,
> +	.cache_type	= REGCACHE_NONE,
> +};
> +
>  struct sun4i_codec_quirks {
>  	const struct regmap_config *regmap_config;
>  	const struct snd_soc_component_driver *codec;
> @@ -1647,6 +1835,15 @@ static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
>  	.has_reset	= true,
>  };
>  
> +static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = {
> +	.regmap_config	= &sun50i_h616_codec_regmap_config,
> +	.codec		= &sun50i_h616_codec_codec,
> +	.create_card	= sun50i_h616_codec_create_card,
> +	.reg_dac_fifoc	= REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31),
> +	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
> +	.has_reset	= true,
> +};
> +
>  static const struct of_device_id sun4i_codec_of_match[] = {
>  	{
>  		.compatible = "allwinner,sun4i-a10-codec",
> @@ -1672,6 +1869,10 @@ static const struct of_device_id sun4i_codec_of_match[] = {
>  		.compatible = "allwinner,sun8i-v3s-codec",
>  		.data = &sun8i_v3s_codec_quirks,
>  	},
> +	{
> +		.compatible = "allwinner,sun50i-h616-codec",
> +		.data = &sun50i_h616_codec_quirks,
> +	},
>  	{}
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> @@ -1860,4 +2061,5 @@ MODULE_AUTHOR("Emilio López <emilio@xxxxxxxxxxxxx>");
>  MODULE_AUTHOR("Jon Smirl <jonsmirl@xxxxxxxxx>");
>  MODULE_AUTHOR("Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx>");
>  MODULE_AUTHOR("Chen-Yu Tsai <wens@xxxxxxxx>");
> +MODULE_AUTHOR("Ryan Walklin <ryan@xxxxxxxxxxxxx");
>  MODULE_LICENSE("GPL");






[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux