162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Audio driver for PCM1789 362306a36Sopenharmony_ci// Copyright (C) 2018 Bootlin 462306a36Sopenharmony_ci// Mylène Josserand <mylene.josserand@bootlin.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/workqueue.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <sound/pcm_params.h> 1162306a36Sopenharmony_ci#include <sound/soc.h> 1262306a36Sopenharmony_ci#include <sound/tlv.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "pcm1789.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define PCM1789_MUTE_CONTROL 0x10 1762306a36Sopenharmony_ci#define PCM1789_FMT_CONTROL 0x11 1862306a36Sopenharmony_ci#define PCM1789_SOFT_MUTE 0x14 1962306a36Sopenharmony_ci#define PCM1789_DAC_VOL_LEFT 0x18 2062306a36Sopenharmony_ci#define PCM1789_DAC_VOL_RIGHT 0x19 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PCM1789_FMT_MASK 0x07 2362306a36Sopenharmony_ci#define PCM1789_MUTE_MASK 0x03 2462306a36Sopenharmony_ci#define PCM1789_MUTE_SRET 0x06 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct pcm1789_private { 2762306a36Sopenharmony_ci struct regmap *regmap; 2862306a36Sopenharmony_ci unsigned int format; 2962306a36Sopenharmony_ci unsigned int rate; 3062306a36Sopenharmony_ci struct gpio_desc *reset; 3162306a36Sopenharmony_ci struct work_struct work; 3262306a36Sopenharmony_ci struct device *dev; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct reg_default pcm1789_reg_defaults[] = { 3662306a36Sopenharmony_ci { PCM1789_FMT_CONTROL, 0x00 }, 3762306a36Sopenharmony_ci { PCM1789_SOFT_MUTE, 0x00 }, 3862306a36Sopenharmony_ci { PCM1789_DAC_VOL_LEFT, 0xff }, 3962306a36Sopenharmony_ci { PCM1789_DAC_VOL_RIGHT, 0xff }, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool pcm1789_accessible_reg(struct device *dev, unsigned int reg) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic bool pcm1789_writeable_reg(struct device *dev, unsigned int reg) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return pcm1789_accessible_reg(dev, reg); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai, 5362306a36Sopenharmony_ci unsigned int format) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 5662306a36Sopenharmony_ci struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci priv->format = format; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 6662306a36Sopenharmony_ci struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE, 6962306a36Sopenharmony_ci PCM1789_MUTE_MASK, 7062306a36Sopenharmony_ci mute ? 0 : PCM1789_MUTE_MASK); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int pcm1789_hw_params(struct snd_pcm_substream *substream, 7462306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 7562306a36Sopenharmony_ci struct snd_soc_dai *codec_dai) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 7862306a36Sopenharmony_ci struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 7962306a36Sopenharmony_ci int val = 0, ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci priv->rate = params_rate(params); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 8462306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 8562306a36Sopenharmony_ci switch (params_width(params)) { 8662306a36Sopenharmony_ci case 24: 8762306a36Sopenharmony_ci val = 2; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci case 16: 9062306a36Sopenharmony_ci val = 3; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci default: 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 9762306a36Sopenharmony_ci switch (params_width(params)) { 9862306a36Sopenharmony_ci case 16: 9962306a36Sopenharmony_ci case 24: 10062306a36Sopenharmony_ci case 32: 10162306a36Sopenharmony_ci val = 0; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci default: 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 10862306a36Sopenharmony_ci switch (params_width(params)) { 10962306a36Sopenharmony_ci case 16: 11062306a36Sopenharmony_ci case 24: 11162306a36Sopenharmony_ci case 32: 11262306a36Sopenharmony_ci val = 1; 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci default: 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci default: 11962306a36Sopenharmony_ci dev_err(component->dev, "Invalid DAI format\n"); 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL, 12462306a36Sopenharmony_ci PCM1789_FMT_MASK, val); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void pcm1789_work_queue(struct work_struct *work) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct pcm1789_private *priv = container_of(work, 13462306a36Sopenharmony_ci struct pcm1789_private, 13562306a36Sopenharmony_ci work); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Perform a software reset to remove codec from desynchronized state */ 13862306a36Sopenharmony_ci if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL, 13962306a36Sopenharmony_ci 0x3 << PCM1789_MUTE_SRET, 0) < 0) 14062306a36Sopenharmony_ci dev_err(priv->dev, "Error while setting SRET"); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, 14462306a36Sopenharmony_ci struct snd_soc_dai *dai) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 14762306a36Sopenharmony_ci struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 14862306a36Sopenharmony_ci int ret = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci switch (cmd) { 15162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 15262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 15362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 15462306a36Sopenharmony_ci schedule_work(&priv->work); 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 15762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 15862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci ret = -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pcm1789_dai_ops = { 16862306a36Sopenharmony_ci .set_fmt = pcm1789_set_dai_fmt, 16962306a36Sopenharmony_ci .hw_params = pcm1789_hw_params, 17062306a36Sopenharmony_ci .mute_stream = pcm1789_mute, 17162306a36Sopenharmony_ci .trigger = pcm1789_trigger, 17262306a36Sopenharmony_ci .no_capture_mute = 1, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const struct snd_kcontrol_new pcm1789_controls[] = { 17862306a36Sopenharmony_ci SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT, 17962306a36Sopenharmony_ci PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, 18062306a36Sopenharmony_ci pcm1789_dac_tlv), 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = { 18462306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("IOUTL+"), 18562306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("IOUTL-"), 18662306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("IOUTR+"), 18762306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("IOUTR-"), 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct snd_soc_dapm_route pcm1789_dapm_routes[] = { 19162306a36Sopenharmony_ci { "IOUTL+", NULL, "Playback" }, 19262306a36Sopenharmony_ci { "IOUTL-", NULL, "Playback" }, 19362306a36Sopenharmony_ci { "IOUTR+", NULL, "Playback" }, 19462306a36Sopenharmony_ci { "IOUTR-", NULL, "Playback" }, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct snd_soc_dai_driver pcm1789_dai = { 19862306a36Sopenharmony_ci .name = "pcm1789-hifi", 19962306a36Sopenharmony_ci .playback = { 20062306a36Sopenharmony_ci .stream_name = "Playback", 20162306a36Sopenharmony_ci .channels_min = 2, 20262306a36Sopenharmony_ci .channels_max = 2, 20362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 20462306a36Sopenharmony_ci .rate_min = 10000, 20562306a36Sopenharmony_ci .rate_max = 200000, 20662306a36Sopenharmony_ci .formats = PCM1789_FORMATS, 20762306a36Sopenharmony_ci }, 20862306a36Sopenharmony_ci .ops = &pcm1789_dai_ops, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciconst struct regmap_config pcm1789_regmap_config = { 21262306a36Sopenharmony_ci .reg_bits = 8, 21362306a36Sopenharmony_ci .val_bits = 8, 21462306a36Sopenharmony_ci .max_register = PCM1789_DAC_VOL_RIGHT, 21562306a36Sopenharmony_ci .reg_defaults = pcm1789_reg_defaults, 21662306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(pcm1789_reg_defaults), 21762306a36Sopenharmony_ci .writeable_reg = pcm1789_writeable_reg, 21862306a36Sopenharmony_ci .readable_reg = pcm1789_accessible_reg, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcm1789_regmap_config); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_pcm1789 = { 22362306a36Sopenharmony_ci .controls = pcm1789_controls, 22462306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(pcm1789_controls), 22562306a36Sopenharmony_ci .dapm_widgets = pcm1789_dapm_widgets, 22662306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(pcm1789_dapm_widgets), 22762306a36Sopenharmony_ci .dapm_routes = pcm1789_dapm_routes, 22862306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(pcm1789_dapm_routes), 22962306a36Sopenharmony_ci .idle_bias_on = 1, 23062306a36Sopenharmony_ci .use_pmdown_time = 1, 23162306a36Sopenharmony_ci .endianness = 1, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciint pcm1789_common_init(struct device *dev, struct regmap *regmap) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct pcm1789_private *pcm1789; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private), 23962306a36Sopenharmony_ci GFP_KERNEL); 24062306a36Sopenharmony_ci if (!pcm1789) 24162306a36Sopenharmony_ci return -ENOMEM; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci pcm1789->regmap = regmap; 24462306a36Sopenharmony_ci pcm1789->dev = dev; 24562306a36Sopenharmony_ci dev_set_drvdata(dev, pcm1789); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 24862306a36Sopenharmony_ci if (IS_ERR(pcm1789->reset)) 24962306a36Sopenharmony_ci return PTR_ERR(pcm1789->reset); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci gpiod_set_value_cansleep(pcm1789->reset, 0); 25262306a36Sopenharmony_ci msleep(300); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci INIT_WORK(&pcm1789->work, pcm1789_work_queue); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789, 25762306a36Sopenharmony_ci &pcm1789_dai, 1); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcm1789_common_init); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_civoid pcm1789_common_exit(struct device *dev) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct pcm1789_private *priv = dev_get_drvdata(dev); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci flush_work(&priv->work); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcm1789_common_exit); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC PCM1789 driver"); 27062306a36Sopenharmony_ciMODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>"); 27162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 272