~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/codecs/tas571x.c

Version: ~ [ linux-5.12-rc7 ] ~ [ linux-5.11.13 ] ~ [ linux-5.10.29 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.111 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.186 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.230 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.266 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.266 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * TAS571x amplifier audio driver
  3  *
  4  * Copyright (C) 2015 Google, Inc.
  5  * Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
  6  *
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License as published by
  9  * the Free Software Foundation; either version 2 of the License, or
 10  * (at your option) any later version.
 11  */
 12 
 13 #include <linux/clk.h>
 14 #include <linux/delay.h>
 15 #include <linux/device.h>
 16 #include <linux/gpio/consumer.h>
 17 #include <linux/i2c.h>
 18 #include <linux/init.h>
 19 #include <linux/kernel.h>
 20 #include <linux/module.h>
 21 #include <linux/of_device.h>
 22 #include <linux/regmap.h>
 23 #include <linux/regulator/consumer.h>
 24 #include <linux/stddef.h>
 25 #include <sound/pcm_params.h>
 26 #include <sound/soc.h>
 27 #include <sound/tlv.h>
 28 
 29 #include "tas571x.h"
 30 
 31 #define TAS571X_MAX_SUPPLIES            6
 32 
 33 struct tas571x_chip {
 34         const char                      *const *supply_names;
 35         int                             num_supply_names;
 36         const struct snd_kcontrol_new   *controls;
 37         int                             num_controls;
 38         const struct regmap_config      *regmap_config;
 39         int                             vol_reg_size;
 40 };
 41 
 42 struct tas571x_private {
 43         const struct tas571x_chip       *chip;
 44         struct regmap                   *regmap;
 45         struct regulator_bulk_data      supplies[TAS571X_MAX_SUPPLIES];
 46         struct clk                      *mclk;
 47         unsigned int                    format;
 48         struct gpio_desc                *reset_gpio;
 49         struct gpio_desc                *pdn_gpio;
 50         struct snd_soc_codec_driver     codec_driver;
 51 };
 52 
 53 static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
 54 {
 55         switch (reg) {
 56         case TAS571X_MVOL_REG:
 57         case TAS571X_CH1_VOL_REG:
 58         case TAS571X_CH2_VOL_REG:
 59                 return priv->chip->vol_reg_size;
 60         default:
 61                 return 1;
 62         }
 63 }
 64 
 65 static int tas571x_reg_write(void *context, unsigned int reg,
 66                              unsigned int value)
 67 {
 68         struct i2c_client *client = context;
 69         struct tas571x_private *priv = i2c_get_clientdata(client);
 70         unsigned int i, size;
 71         uint8_t buf[5];
 72         int ret;
 73 
 74         size = tas571x_register_size(priv, reg);
 75         buf[0] = reg;
 76 
 77         for (i = size; i >= 1; --i) {
 78                 buf[i] = value;
 79                 value >>= 8;
 80         }
 81 
 82         ret = i2c_master_send(client, buf, size + 1);
 83         if (ret == size + 1)
 84                 return 0;
 85         else if (ret < 0)
 86                 return ret;
 87         else
 88                 return -EIO;
 89 }
 90 
 91 static int tas571x_reg_read(void *context, unsigned int reg,
 92                             unsigned int *value)
 93 {
 94         struct i2c_client *client = context;
 95         struct tas571x_private *priv = i2c_get_clientdata(client);
 96         uint8_t send_buf, recv_buf[4];
 97         struct i2c_msg msgs[2];
 98         unsigned int size;
 99         unsigned int i;
100         int ret;
101 
102         size = tas571x_register_size(priv, reg);
103         send_buf = reg;
104 
105         msgs[0].addr = client->addr;
106         msgs[0].len = sizeof(send_buf);
107         msgs[0].buf = &send_buf;
108         msgs[0].flags = 0;
109 
110         msgs[1].addr = client->addr;
111         msgs[1].len = size;
112         msgs[1].buf = recv_buf;
113         msgs[1].flags = I2C_M_RD;
114 
115         ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
116         if (ret < 0)
117                 return ret;
118         else if (ret != ARRAY_SIZE(msgs))
119                 return -EIO;
120 
121         *value = 0;
122 
123         for (i = 0; i < size; i++) {
124                 *value <<= 8;
125                 *value |= recv_buf[i];
126         }
127 
128         return 0;
129 }
130 
131 static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
132 {
133         struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
134 
135         priv->format = format;
136 
137         return 0;
138 }
139 
140 static int tas571x_hw_params(struct snd_pcm_substream *substream,
141                              struct snd_pcm_hw_params *params,
142                              struct snd_soc_dai *dai)
143 {
144         struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
145         u32 val;
146 
147         switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
148         case SND_SOC_DAIFMT_RIGHT_J:
149                 val = 0x00;
150                 break;
151         case SND_SOC_DAIFMT_I2S:
152                 val = 0x03;
153                 break;
154         case SND_SOC_DAIFMT_LEFT_J:
155                 val = 0x06;
156                 break;
157         default:
158                 return -EINVAL;
159         }
160 
161         if (params_width(params) >= 24)
162                 val += 2;
163         else if (params_width(params) >= 20)
164                 val += 1;
165 
166         return regmap_update_bits(priv->regmap, TAS571X_SDI_REG,
167                                   TAS571X_SDI_FMT_MASK, val);
168 }
169 
170 static int tas571x_set_bias_level(struct snd_soc_codec *codec,
171                                   enum snd_soc_bias_level level)
172 {
173         struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec);
174         int ret;
175 
176         switch (level) {
177         case SND_SOC_BIAS_ON:
178                 break;
179         case SND_SOC_BIAS_PREPARE:
180                 break;
181         case SND_SOC_BIAS_STANDBY:
182                 if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
183                         if (!IS_ERR(priv->mclk)) {
184                                 ret = clk_prepare_enable(priv->mclk);
185                                 if (ret) {
186                                         dev_err(codec->dev,
187                                                 "Failed to enable master clock: %d\n",
188                                                 ret);
189                                         return ret;
190                                 }
191                         }
192 
193                         gpiod_set_value(priv->pdn_gpio, 0);
194                         usleep_range(5000, 6000);
195 
196                         regcache_cache_only(priv->regmap, false);
197                         ret = regcache_sync(priv->regmap);
198                         if (ret)
199                                 return ret;
200                 }
201                 break;
202         case SND_SOC_BIAS_OFF:
203                 regcache_cache_only(priv->regmap, true);
204                 gpiod_set_value(priv->pdn_gpio, 1);
205 
206                 if (!IS_ERR(priv->mclk))
207                         clk_disable_unprepare(priv->mclk);
208                 break;
209         }
210 
211         return 0;
212 }
213 
214 static const struct snd_soc_dai_ops tas571x_dai_ops = {
215         .set_fmt        = tas571x_set_dai_fmt,
216         .hw_params      = tas571x_hw_params,
217 };
218 
219 static const char *const tas5711_supply_names[] = {
220         "AVDD",
221         "DVDD",
222         "PVDD_A",
223         "PVDD_B",
224         "PVDD_C",
225         "PVDD_D",
226 };
227 
228 static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1);
229 
230 static const struct snd_kcontrol_new tas5711_controls[] = {
231         SOC_SINGLE_TLV("Master Volume",
232                        TAS571X_MVOL_REG,
233                        0, 0xff, 1, tas5711_volume_tlv),
234         SOC_DOUBLE_R_TLV("Speaker Volume",
235                          TAS571X_CH1_VOL_REG,
236                          TAS571X_CH2_VOL_REG,
237                          0, 0xff, 1, tas5711_volume_tlv),
238         SOC_DOUBLE("Speaker Switch",
239                    TAS571X_SOFT_MUTE_REG,
240                    TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
241                    1, 1),
242 };
243 
244 static const struct reg_default tas5711_reg_defaults[] = {
245         { 0x04, 0x05 },
246         { 0x05, 0x40 },
247         { 0x06, 0x00 },
248         { 0x07, 0xff },
249         { 0x08, 0x30 },
250         { 0x09, 0x30 },
251         { 0x1b, 0x82 },
252 };
253 
254 static const struct regmap_config tas5711_regmap_config = {
255         .reg_bits                       = 8,
256         .val_bits                       = 32,
257         .max_register                   = 0xff,
258         .reg_read                       = tas571x_reg_read,
259         .reg_write                      = tas571x_reg_write,
260         .reg_defaults                   = tas5711_reg_defaults,
261         .num_reg_defaults               = ARRAY_SIZE(tas5711_reg_defaults),
262         .cache_type                     = REGCACHE_RBTREE,
263 };
264 
265 static const struct tas571x_chip tas5711_chip = {
266         .supply_names                   = tas5711_supply_names,
267         .num_supply_names               = ARRAY_SIZE(tas5711_supply_names),
268         .controls                       = tas5711_controls,
269         .num_controls                   = ARRAY_SIZE(tas5711_controls),
270         .regmap_config                  = &tas5711_regmap_config,
271         .vol_reg_size                   = 1,
272 };
273 
274 static const char *const tas5717_supply_names[] = {
275         "AVDD",
276         "DVDD",
277         "HPVDD",
278         "PVDD_AB",
279         "PVDD_CD",
280 };
281 
282 static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0);
283 
284 static const struct snd_kcontrol_new tas5717_controls[] = {
285         /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */
286         SOC_SINGLE_TLV("Master Volume",
287                        TAS571X_MVOL_REG, 1, 0x1ff, 1,
288                        tas5717_volume_tlv),
289         SOC_DOUBLE_R_TLV("Speaker Volume",
290                          TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,
291                          1, 0x1ff, 1, tas5717_volume_tlv),
292         SOC_DOUBLE("Speaker Switch",
293                    TAS571X_SOFT_MUTE_REG,
294                    TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
295                    1, 1),
296 };
297 
298 static const struct reg_default tas5717_reg_defaults[] = {
299         { 0x04, 0x05 },
300         { 0x05, 0x40 },
301         { 0x06, 0x00 },
302         { 0x07, 0x03ff },
303         { 0x08, 0x00c0 },
304         { 0x09, 0x00c0 },
305         { 0x1b, 0x82 },
306 };
307 
308 static const struct regmap_config tas5717_regmap_config = {
309         .reg_bits                       = 8,
310         .val_bits                       = 32,
311         .max_register                   = 0xff,
312         .reg_read                       = tas571x_reg_read,
313         .reg_write                      = tas571x_reg_write,
314         .reg_defaults                   = tas5717_reg_defaults,
315         .num_reg_defaults               = ARRAY_SIZE(tas5717_reg_defaults),
316         .cache_type                     = REGCACHE_RBTREE,
317 };
318 
319 /* This entry is reused for tas5719 as the software interface is identical. */
320 static const struct tas571x_chip tas5717_chip = {
321         .supply_names                   = tas5717_supply_names,
322         .num_supply_names               = ARRAY_SIZE(tas5717_supply_names),
323         .controls                       = tas5717_controls,
324         .num_controls                   = ARRAY_SIZE(tas5717_controls),
325         .regmap_config                  = &tas5717_regmap_config,
326         .vol_reg_size                   = 2,
327 };
328 
329 static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = {
330         SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
331         SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
332 
333         SND_SOC_DAPM_OUTPUT("OUT_A"),
334         SND_SOC_DAPM_OUTPUT("OUT_B"),
335         SND_SOC_DAPM_OUTPUT("OUT_C"),
336         SND_SOC_DAPM_OUTPUT("OUT_D"),
337 };
338 
339 static const struct snd_soc_dapm_route tas571x_dapm_routes[] = {
340         { "DACL",  NULL, "Playback" },
341         { "DACR",  NULL, "Playback" },
342 
343         { "OUT_A", NULL, "DACL" },
344         { "OUT_B", NULL, "DACL" },
345         { "OUT_C", NULL, "DACR" },
346         { "OUT_D", NULL, "DACR" },
347 };
348 
349 static const struct snd_soc_codec_driver tas571x_codec = {
350         .set_bias_level = tas571x_set_bias_level,
351         .idle_bias_off = true,
352 
353         .dapm_widgets = tas571x_dapm_widgets,
354         .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets),
355         .dapm_routes = tas571x_dapm_routes,
356         .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes),
357 };
358 
359 static struct snd_soc_dai_driver tas571x_dai = {
360         .name = "tas571x-hifi",
361         .playback = {
362                 .stream_name = "Playback",
363                 .channels_min = 2,
364                 .channels_max = 2,
365                 .rates = SNDRV_PCM_RATE_8000_48000,
366                 .formats = SNDRV_PCM_FMTBIT_S32_LE |
367                            SNDRV_PCM_FMTBIT_S24_LE |
368                            SNDRV_PCM_FMTBIT_S16_LE,
369         },
370         .ops = &tas571x_dai_ops,
371 };
372 
373 static const struct of_device_id tas571x_of_match[];
374 
375 static int tas571x_i2c_probe(struct i2c_client *client,
376                              const struct i2c_device_id *id)
377 {
378         struct tas571x_private *priv;
379         struct device *dev = &client->dev;
380         const struct of_device_id *of_id;
381         int i, ret;
382 
383         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
384         if (!priv)
385                 return -ENOMEM;
386         i2c_set_clientdata(client, priv);
387 
388         of_id = of_match_device(tas571x_of_match, dev);
389         if (!of_id) {
390                 dev_err(dev, "Unknown device type\n");
391                 return -EINVAL;
392         }
393         priv->chip = of_id->data;
394 
395         priv->mclk = devm_clk_get(dev, "mclk");
396         if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
397                 dev_err(dev, "Failed to request mclk: %ld\n",
398                         PTR_ERR(priv->mclk));
399                 return PTR_ERR(priv->mclk);
400         }
401 
402         BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES);
403         for (i = 0; i < priv->chip->num_supply_names; i++)
404                 priv->supplies[i].supply = priv->chip->supply_names[i];
405 
406         ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names,
407                                       priv->supplies);
408         if (ret) {
409                 dev_err(dev, "Failed to get supplies: %d\n", ret);
410                 return ret;
411         }
412         ret = regulator_bulk_enable(priv->chip->num_supply_names,
413                                     priv->supplies);
414         if (ret) {
415                 dev_err(dev, "Failed to enable supplies: %d\n", ret);
416                 return ret;
417         }
418 
419         priv->regmap = devm_regmap_init(dev, NULL, client,
420                                         priv->chip->regmap_config);
421         if (IS_ERR(priv->regmap))
422                 return PTR_ERR(priv->regmap);
423 
424         priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
425         if (IS_ERR(priv->pdn_gpio)) {
426                 dev_err(dev, "error requesting pdn_gpio: %ld\n",
427                         PTR_ERR(priv->pdn_gpio));
428                 return PTR_ERR(priv->pdn_gpio);
429         }
430 
431         priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
432                                                    GPIOD_OUT_HIGH);
433         if (IS_ERR(priv->reset_gpio)) {
434                 dev_err(dev, "error requesting reset_gpio: %ld\n",
435                         PTR_ERR(priv->reset_gpio));
436                 return PTR_ERR(priv->reset_gpio);
437         } else if (priv->reset_gpio) {
438                 /* pulse the active low reset line for ~100us */
439                 usleep_range(100, 200);
440                 gpiod_set_value(priv->reset_gpio, 0);
441                 usleep_range(12000, 20000);
442         }
443 
444         ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
445         if (ret)
446                 return ret;
447 
448         ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
449                                  TAS571X_SYS_CTRL_2_SDN_MASK, 0);
450         if (ret)
451                 return ret;
452 
453         memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
454         priv->codec_driver.controls = priv->chip->controls;
455         priv->codec_driver.num_controls = priv->chip->num_controls;
456 
457         if (priv->chip->vol_reg_size == 2) {
458                 /*
459                  * The master volume defaults to 0x3ff (mute), but we ignore
460                  * (zero) the LSB because the hardware step size is 0.125 dB
461                  * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB.
462                  */
463                 ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
464                 if (ret)
465                         return ret;
466         }
467 
468         regcache_cache_only(priv->regmap, true);
469         gpiod_set_value(priv->pdn_gpio, 1);
470 
471         return snd_soc_register_codec(&client->dev, &priv->codec_driver,
472                                       &tas571x_dai, 1);
473 }
474 
475 static int tas571x_i2c_remove(struct i2c_client *client)
476 {
477         struct tas571x_private *priv = i2c_get_clientdata(client);
478 
479         snd_soc_unregister_codec(&client->dev);
480         regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
481 
482         return 0;
483 }
484 
485 static const struct of_device_id tas571x_of_match[] = {
486         { .compatible = "ti,tas5711", .data = &tas5711_chip, },
487         { .compatible = "ti,tas5717", .data = &tas5717_chip, },
488         { .compatible = "ti,tas5719", .data = &tas5717_chip, },
489         { }
490 };
491 MODULE_DEVICE_TABLE(of, tas571x_of_match);
492 
493 static const struct i2c_device_id tas571x_i2c_id[] = {
494         { "tas5711", 0 },
495         { "tas5717", 0 },
496         { "tas5719", 0 },
497         { }
498 };
499 MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
500 
501 static struct i2c_driver tas571x_i2c_driver = {
502         .driver = {
503                 .name = "tas571x",
504                 .of_match_table = of_match_ptr(tas571x_of_match),
505         },
506         .probe = tas571x_i2c_probe,
507         .remove = tas571x_i2c_remove,
508         .id_table = tas571x_i2c_id,
509 };
510 module_i2c_driver(tas571x_i2c_driver);
511 
512 MODULE_DESCRIPTION("ASoC TAS571x driver");
513 MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>");
514 MODULE_LICENSE("GPL");
515 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp