xref: /openbmc/linux/sound/soc/codecs/pcm179x.c (revision dea54fba)
1 /*
2  * PCM179X ASoC codec driver
3  *
4  * Copyright (c) Amarula Solutions B.V. 2013
5  *
6  *     Michael Trimarchi <michael@amarulasolutions.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18 
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <linux/kernel.h>
22 #include <linux/device.h>
23 
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/initval.h>
28 #include <sound/soc.h>
29 #include <sound/tlv.h>
30 #include <linux/of.h>
31 
32 #include "pcm179x.h"
33 
34 #define PCM179X_DAC_VOL_LEFT	0x10
35 #define PCM179X_DAC_VOL_RIGHT	0x11
36 #define PCM179X_FMT_CONTROL	0x12
37 #define PCM179X_MODE_CONTROL	0x13
38 #define PCM179X_SOFT_MUTE	PCM179X_FMT_CONTROL
39 
40 #define PCM179X_FMT_MASK	0x70
41 #define PCM179X_FMT_SHIFT	4
42 #define PCM179X_MUTE_MASK	0x01
43 #define PCM179X_MUTE_SHIFT	0
44 #define PCM179X_ATLD_ENABLE	(1 << 7)
45 
46 static const struct reg_default pcm179x_reg_defaults[] = {
47 	{ 0x10, 0xff },
48 	{ 0x11, 0xff },
49 	{ 0x12, 0x50 },
50 	{ 0x13, 0x00 },
51 	{ 0x14, 0x00 },
52 	{ 0x15, 0x01 },
53 	{ 0x16, 0x00 },
54 	{ 0x17, 0x00 },
55 };
56 
57 static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
58 {
59 	return reg >= 0x10 && reg <= 0x17;
60 }
61 
62 static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
63 {
64 	bool accessible;
65 
66 	accessible = pcm179x_accessible_reg(dev, reg);
67 
68 	return accessible && reg != 0x16 && reg != 0x17;
69 }
70 
71 struct pcm179x_private {
72 	struct regmap *regmap;
73 	unsigned int format;
74 	unsigned int rate;
75 };
76 
77 static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
78                              unsigned int format)
79 {
80 	struct snd_soc_codec *codec = codec_dai->codec;
81 	struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
82 
83 	priv->format = format;
84 
85 	return 0;
86 }
87 
88 static int pcm179x_digital_mute(struct snd_soc_dai *dai, int mute)
89 {
90 	struct snd_soc_codec *codec = dai->codec;
91 	struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
92 	int ret;
93 
94 	ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
95 				 PCM179X_MUTE_MASK, !!mute);
96 	if (ret < 0)
97 		return ret;
98 
99 	return 0;
100 }
101 
102 static int pcm179x_hw_params(struct snd_pcm_substream *substream,
103 			     struct snd_pcm_hw_params *params,
104 			     struct snd_soc_dai *dai)
105 {
106 	struct snd_soc_codec *codec = dai->codec;
107 	struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
108 	int val = 0, ret;
109 
110 	priv->rate = params_rate(params);
111 
112 	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
113 	case SND_SOC_DAIFMT_RIGHT_J:
114 		switch (params_width(params)) {
115 		case 24:
116 		case 32:
117 			val = 2;
118 			break;
119 		case 16:
120 			val = 0;
121 			break;
122 		default:
123 			return -EINVAL;
124 		}
125 		break;
126 	case SND_SOC_DAIFMT_I2S:
127 		switch (params_width(params)) {
128 		case 24:
129 		case 32:
130 			val = 5;
131 			break;
132 		case 16:
133 			val = 4;
134 			break;
135 		default:
136 			return -EINVAL;
137 		}
138 		break;
139 	default:
140 		dev_err(codec->dev, "Invalid DAI format\n");
141 		return -EINVAL;
142 	}
143 
144 	val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
145 
146 	ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
147 				 PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
148 	if (ret < 0)
149 		return ret;
150 
151 	return 0;
152 }
153 
154 static const struct snd_soc_dai_ops pcm179x_dai_ops = {
155 	.set_fmt	= pcm179x_set_dai_fmt,
156 	.hw_params	= pcm179x_hw_params,
157 	.digital_mute	= pcm179x_digital_mute,
158 };
159 
160 static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
161 
162 static const struct snd_kcontrol_new pcm179x_controls[] = {
163 	SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
164 			 PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
165 			 pcm179x_dac_tlv),
166 	SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
167 	SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
168 };
169 
170 static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
171 SND_SOC_DAPM_OUTPUT("IOUTL+"),
172 SND_SOC_DAPM_OUTPUT("IOUTL-"),
173 SND_SOC_DAPM_OUTPUT("IOUTR+"),
174 SND_SOC_DAPM_OUTPUT("IOUTR-"),
175 };
176 
177 static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
178 	{ "IOUTL+", NULL, "Playback" },
179 	{ "IOUTL-", NULL, "Playback" },
180 	{ "IOUTR+", NULL, "Playback" },
181 	{ "IOUTR-", NULL, "Playback" },
182 };
183 
184 static struct snd_soc_dai_driver pcm179x_dai = {
185 	.name = "pcm179x-hifi",
186 	.playback = {
187 		.stream_name = "Playback",
188 		.channels_min = 2,
189 		.channels_max = 2,
190 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
191 		.rate_min = 10000,
192 		.rate_max = 200000,
193 		.formats = PCM1792A_FORMATS, },
194 	.ops = &pcm179x_dai_ops,
195 };
196 
197 const struct regmap_config pcm179x_regmap_config = {
198 	.reg_bits		= 8,
199 	.val_bits		= 8,
200 	.max_register		= 23,
201 	.reg_defaults		= pcm179x_reg_defaults,
202 	.num_reg_defaults	= ARRAY_SIZE(pcm179x_reg_defaults),
203 	.writeable_reg		= pcm179x_writeable_reg,
204 	.readable_reg		= pcm179x_accessible_reg,
205 };
206 EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
207 
208 static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
209 	.component_driver = {
210 		.controls		= pcm179x_controls,
211 		.num_controls		= ARRAY_SIZE(pcm179x_controls),
212 		.dapm_widgets		= pcm179x_dapm_widgets,
213 		.num_dapm_widgets	= ARRAY_SIZE(pcm179x_dapm_widgets),
214 		.dapm_routes		= pcm179x_dapm_routes,
215 		.num_dapm_routes	= ARRAY_SIZE(pcm179x_dapm_routes),
216 	},
217 };
218 
219 int pcm179x_common_init(struct device *dev, struct regmap *regmap)
220 {
221 	struct pcm179x_private *pcm179x;
222 
223 	pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
224 				GFP_KERNEL);
225 	if (!pcm179x)
226 		return -ENOMEM;
227 
228 	pcm179x->regmap = regmap;
229 	dev_set_drvdata(dev, pcm179x);
230 
231 	return snd_soc_register_codec(dev,
232 			&soc_codec_dev_pcm179x, &pcm179x_dai, 1);
233 }
234 EXPORT_SYMBOL_GPL(pcm179x_common_init);
235 
236 int pcm179x_common_exit(struct device *dev)
237 {
238 	snd_soc_unregister_codec(dev);
239 	return 0;
240 }
241 EXPORT_SYMBOL_GPL(pcm179x_common_exit);
242 
243 MODULE_DESCRIPTION("ASoC PCM179X driver");
244 MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
245 MODULE_LICENSE("GPL");
246