brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 3ab381e Raw
272 lines · c
1// SPDX-License-Identifier: GPL-2.02// Audio driver for PCM17893// Copyright (C) 2018 Bootlin4// Mylène Josserand <mylene.josserand@bootlin.com>5 6#include <linux/gpio/consumer.h>7#include <linux/module.h>8#include <linux/workqueue.h>9 10#include <sound/pcm_params.h>11#include <sound/soc.h>12#include <sound/tlv.h>13 14#include "pcm1789.h"15 16#define PCM1789_MUTE_CONTROL	0x1017#define PCM1789_FMT_CONTROL	0x1118#define PCM1789_SOFT_MUTE	0x1419#define PCM1789_DAC_VOL_LEFT	0x1820#define PCM1789_DAC_VOL_RIGHT	0x1921 22#define PCM1789_FMT_MASK	0x0723#define PCM1789_MUTE_MASK	0x0324#define PCM1789_MUTE_SRET	0x0625 26struct pcm1789_private {27	struct regmap *regmap;28	unsigned int format;29	unsigned int rate;30	struct gpio_desc *reset;31	struct work_struct work;32	struct device *dev;33};34 35static const struct reg_default pcm1789_reg_defaults[] = {36	{ PCM1789_FMT_CONTROL, 0x00 },37	{ PCM1789_SOFT_MUTE, 0x00 },38	{ PCM1789_DAC_VOL_LEFT, 0xff },39	{ PCM1789_DAC_VOL_RIGHT, 0xff },40};41 42static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg)43{44	return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT;45}46 47static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg)48{49	return pcm1789_accessible_reg(dev, reg);50}51 52static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai,53			       unsigned int format)54{55	struct snd_soc_component *component = codec_dai->component;56	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);57 58	priv->format = format;59 60	return 0;61}62 63static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction)64{65	struct snd_soc_component *component = codec_dai->component;66	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);67 68	return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE,69				  PCM1789_MUTE_MASK,70				  mute ? 0 : PCM1789_MUTE_MASK);71}72 73static int pcm1789_hw_params(struct snd_pcm_substream *substream,74			     struct snd_pcm_hw_params *params,75			     struct snd_soc_dai *codec_dai)76{77	struct snd_soc_component *component = codec_dai->component;78	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);79	int val = 0, ret;80 81	priv->rate = params_rate(params);82 83	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {84	case SND_SOC_DAIFMT_RIGHT_J:85		switch (params_width(params)) {86		case 24:87			val = 2;88			break;89		case 16:90			val = 3;91			break;92		default:93			return -EINVAL;94		}95		break;96	case SND_SOC_DAIFMT_I2S:97		switch (params_width(params)) {98		case 16:99		case 24:100		case 32:101			val = 0;102			break;103		default:104			return -EINVAL;105		}106		break;107	case SND_SOC_DAIFMT_LEFT_J:108		switch (params_width(params)) {109		case 16:110		case 24:111		case 32:112			val = 1;113			break;114		default:115			return -EINVAL;116		}117		break;118	default:119		dev_err(component->dev, "Invalid DAI format\n");120		return -EINVAL;121	}122 123	ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL,124				 PCM1789_FMT_MASK, val);125	if (ret < 0)126		return ret;127 128	return 0;129}130 131static void pcm1789_work_queue(struct work_struct *work)132{133	struct pcm1789_private *priv = container_of(work,134						    struct pcm1789_private,135						    work);136 137	/* Perform a software reset to remove codec from desynchronized state */138	if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL,139			       0x3 << PCM1789_MUTE_SRET, 0) < 0)140		dev_err(priv->dev, "Error while setting SRET");141}142 143static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd,144			   struct snd_soc_dai *dai)145{146	struct snd_soc_component *component = dai->component;147	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);148	int ret = 0;149 150	switch (cmd) {151	case SNDRV_PCM_TRIGGER_START:152	case SNDRV_PCM_TRIGGER_RESUME:153	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:154		schedule_work(&priv->work);155		break;156	case SNDRV_PCM_TRIGGER_STOP:157	case SNDRV_PCM_TRIGGER_SUSPEND:158	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:159		break;160	default:161		ret = -EINVAL;162	}163 164	return ret;165}166 167static const struct snd_soc_dai_ops pcm1789_dai_ops = {168	.set_fmt	= pcm1789_set_dai_fmt,169	.hw_params	= pcm1789_hw_params,170	.mute_stream	= pcm1789_mute,171	.trigger	= pcm1789_trigger,172	.no_capture_mute = 1,173};174 175static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1);176 177static const struct snd_kcontrol_new pcm1789_controls[] = {178	SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT,179			       PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,180			       pcm1789_dac_tlv),181};182 183static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = {184	SND_SOC_DAPM_OUTPUT("IOUTL+"),185	SND_SOC_DAPM_OUTPUT("IOUTL-"),186	SND_SOC_DAPM_OUTPUT("IOUTR+"),187	SND_SOC_DAPM_OUTPUT("IOUTR-"),188};189 190static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = {191	{ "IOUTL+", NULL, "Playback" },192	{ "IOUTL-", NULL, "Playback" },193	{ "IOUTR+", NULL, "Playback" },194	{ "IOUTR-", NULL, "Playback" },195};196 197static struct snd_soc_dai_driver pcm1789_dai = {198	.name = "pcm1789-hifi",199	.playback = {200		.stream_name = "Playback",201		.channels_min = 2,202		.channels_max = 2,203		.rates = SNDRV_PCM_RATE_CONTINUOUS,204		.rate_min = 10000,205		.rate_max = 200000,206		.formats = PCM1789_FORMATS,207	},208	.ops = &pcm1789_dai_ops,209};210 211const struct regmap_config pcm1789_regmap_config = {212	.reg_bits		= 8,213	.val_bits		= 8,214	.max_register		= PCM1789_DAC_VOL_RIGHT,215	.reg_defaults		= pcm1789_reg_defaults,216	.num_reg_defaults	= ARRAY_SIZE(pcm1789_reg_defaults),217	.writeable_reg		= pcm1789_writeable_reg,218	.readable_reg		= pcm1789_accessible_reg,219};220EXPORT_SYMBOL_GPL(pcm1789_regmap_config);221 222static const struct snd_soc_component_driver soc_component_dev_pcm1789 = {223	.controls		= pcm1789_controls,224	.num_controls		= ARRAY_SIZE(pcm1789_controls),225	.dapm_widgets		= pcm1789_dapm_widgets,226	.num_dapm_widgets	= ARRAY_SIZE(pcm1789_dapm_widgets),227	.dapm_routes		= pcm1789_dapm_routes,228	.num_dapm_routes	= ARRAY_SIZE(pcm1789_dapm_routes),229	.idle_bias_on		= 1,230	.use_pmdown_time	= 1,231	.endianness		= 1,232};233 234int pcm1789_common_init(struct device *dev, struct regmap *regmap)235{236	struct pcm1789_private *pcm1789;237 238	pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private),239			       GFP_KERNEL);240	if (!pcm1789)241		return -ENOMEM;242 243	pcm1789->regmap = regmap;244	pcm1789->dev = dev;245	dev_set_drvdata(dev, pcm1789);246 247	pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);248	if (IS_ERR(pcm1789->reset))249		return PTR_ERR(pcm1789->reset);250 251	gpiod_set_value_cansleep(pcm1789->reset, 0);252	msleep(300);253 254	INIT_WORK(&pcm1789->work, pcm1789_work_queue);255 256	return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789,257					       &pcm1789_dai, 1);258}259EXPORT_SYMBOL_GPL(pcm1789_common_init);260 261void pcm1789_common_exit(struct device *dev)262{263	struct pcm1789_private *priv = dev_get_drvdata(dev);264 265	flush_work(&priv->work);266}267EXPORT_SYMBOL_GPL(pcm1789_common_exit);268 269MODULE_DESCRIPTION("ASoC PCM1789 driver");270MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");271MODULE_LICENSE("GPL");272