diff options
author | Damien.Horsley <Damien.Horsley@imgtec.com> | 2015-12-08 10:59:00 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-12-12 17:39:59 -0500 |
commit | a9b17a638af5ae374677c5349653114231483419 (patch) | |
tree | 3314843eada3fb29278070248115caf28544eb89 | |
parent | ccfa2bef0f3bb024fc6efb1501177dddbb003c06 (diff) |
ASoC: pcm3168a: Add driver for pcm3168a codec
Add driver for Texas Instruments pcm3168a codec
Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/Kconfig | 17 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/codecs/pcm3168a-i2c.c | 66 | ||||
-rw-r--r-- | sound/soc/codecs/pcm3168a-spi.c | 65 | ||||
-rw-r--r-- | sound/soc/codecs/pcm3168a.c | 767 | ||||
-rw-r--r-- | sound/soc/codecs/pcm3168a.h | 100 |
6 files changed, 1021 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cfdafc4c11ea..012bdcf6d175 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -85,6 +85,8 @@ config SND_SOC_ALL_CODECS | |||
85 | select SND_SOC_PCM1681 if I2C | 85 | select SND_SOC_PCM1681 if I2C |
86 | select SND_SOC_PCM1792A if SPI_MASTER | 86 | select SND_SOC_PCM1792A if SPI_MASTER |
87 | select SND_SOC_PCM3008 | 87 | select SND_SOC_PCM3008 |
88 | select SND_SOC_PCM3168A_I2C if I2C | ||
89 | select SND_SOC_PCM3168A_SPI if SPI_MASTER | ||
88 | select SND_SOC_PCM512x_I2C if I2C | 90 | select SND_SOC_PCM512x_I2C if I2C |
89 | select SND_SOC_PCM512x_SPI if SPI_MASTER | 91 | select SND_SOC_PCM512x_SPI if SPI_MASTER |
90 | select SND_SOC_RT286 if I2C | 92 | select SND_SOC_RT286 if I2C |
@@ -506,6 +508,21 @@ config SND_SOC_PCM1792A | |||
506 | config SND_SOC_PCM3008 | 508 | config SND_SOC_PCM3008 |
507 | tristate | 509 | tristate |
508 | 510 | ||
511 | config SND_SOC_PCM3168A | ||
512 | tristate | ||
513 | |||
514 | config SND_SOC_PCM3168A_I2C | ||
515 | tristate "Texas Instruments PCM3168A CODEC - I2C" | ||
516 | depends on I2C | ||
517 | select SND_SOC_PCM3168A | ||
518 | select REGMAP_I2C | ||
519 | |||
520 | config SND_SOC_PCM3168A_SPI | ||
521 | tristate "Texas Instruments PCM3168A CODEC - SPI" | ||
522 | depends on SPI_MASTER | ||
523 | select SND_SOC_PCM3168A | ||
524 | select REGMAP_SPI | ||
525 | |||
509 | config SND_SOC_PCM512x | 526 | config SND_SOC_PCM512x |
510 | tristate | 527 | tristate |
511 | 528 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f632fc42f59f..890ae5571a00 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -78,6 +78,9 @@ snd-soc-nau8825-objs := nau8825.o | |||
78 | snd-soc-pcm1681-objs := pcm1681.o | 78 | snd-soc-pcm1681-objs := pcm1681.o |
79 | snd-soc-pcm1792a-codec-objs := pcm1792a.o | 79 | snd-soc-pcm1792a-codec-objs := pcm1792a.o |
80 | snd-soc-pcm3008-objs := pcm3008.o | 80 | snd-soc-pcm3008-objs := pcm3008.o |
81 | snd-soc-pcm3168a-objs := pcm3168a.o | ||
82 | snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o | ||
83 | snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o | ||
81 | snd-soc-pcm512x-objs := pcm512x.o | 84 | snd-soc-pcm512x-objs := pcm512x.o |
82 | snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o | 85 | snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o |
83 | snd-soc-pcm512x-spi-objs := pcm512x-spi.o | 86 | snd-soc-pcm512x-spi-objs := pcm512x-spi.o |
@@ -273,6 +276,9 @@ obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o | |||
273 | obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o | 276 | obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o |
274 | obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o | 277 | obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o |
275 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o | 278 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
279 | obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o | ||
280 | obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o | ||
281 | obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o | ||
276 | obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o | 282 | obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o |
277 | obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o | 283 | obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o |
278 | obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o | 284 | obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o |
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c new file mode 100644 index 000000000000..6feb0901dfeb --- /dev/null +++ b/sound/soc/codecs/pcm3168a-i2c.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * PCM3168A codec i2c driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * Author: Damien Horsley <Damien.Horsley@imgtec.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/i2c.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | |||
17 | #include <sound/soc.h> | ||
18 | |||
19 | #include "pcm3168a.h" | ||
20 | |||
21 | static int pcm3168a_i2c_probe(struct i2c_client *i2c, | ||
22 | const struct i2c_device_id *id) | ||
23 | { | ||
24 | struct regmap *regmap; | ||
25 | |||
26 | regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap); | ||
27 | if (IS_ERR(regmap)) | ||
28 | return PTR_ERR(regmap); | ||
29 | |||
30 | return pcm3168a_probe(&i2c->dev, regmap); | ||
31 | } | ||
32 | |||
33 | static int pcm3168a_i2c_remove(struct i2c_client *i2c) | ||
34 | { | ||
35 | pcm3168a_remove(&i2c->dev); | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static const struct i2c_device_id pcm3168a_i2c_id[] = { | ||
41 | { "pcm3168a", }, | ||
42 | { } | ||
43 | }; | ||
44 | MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); | ||
45 | |||
46 | static const struct of_device_id pcm3168a_of_match[] = { | ||
47 | { .compatible = "ti,pcm3168a", }, | ||
48 | { } | ||
49 | }; | ||
50 | MODULE_DEVICE_TABLE(of, pcm3168a_of_match); | ||
51 | |||
52 | static struct i2c_driver pcm3168a_i2c_driver = { | ||
53 | .probe = pcm3168a_i2c_probe, | ||
54 | .remove = pcm3168a_i2c_remove, | ||
55 | .id_table = pcm3168a_i2c_id, | ||
56 | .driver = { | ||
57 | .name = "pcm3168a", | ||
58 | .of_match_table = pcm3168a_of_match, | ||
59 | .pm = &pcm3168a_pm_ops, | ||
60 | }, | ||
61 | }; | ||
62 | module_i2c_driver(pcm3168a_i2c_driver); | ||
63 | |||
64 | MODULE_DESCRIPTION("PCM3168A I2C codec driver"); | ||
65 | MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); | ||
66 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c new file mode 100644 index 000000000000..03945a27ae40 --- /dev/null +++ b/sound/soc/codecs/pcm3168a-spi.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * PCM3168A codec spi driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * Author: Damien Horsley <Damien.Horsley@imgtec.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | |||
17 | #include <sound/soc.h> | ||
18 | |||
19 | #include "pcm3168a.h" | ||
20 | |||
21 | static int pcm3168a_spi_probe(struct spi_device *spi) | ||
22 | { | ||
23 | struct regmap *regmap; | ||
24 | |||
25 | regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap); | ||
26 | if (IS_ERR(regmap)) | ||
27 | return PTR_ERR(regmap); | ||
28 | |||
29 | return pcm3168a_probe(&spi->dev, regmap); | ||
30 | } | ||
31 | |||
32 | static int pcm3168a_spi_remove(struct spi_device *spi) | ||
33 | { | ||
34 | pcm3168a_remove(&spi->dev); | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static const struct spi_device_id pcm3168a_spi_id[] = { | ||
40 | { "pcm3168a", }, | ||
41 | { }, | ||
42 | }; | ||
43 | MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id); | ||
44 | |||
45 | static const struct of_device_id pcm3168a_of_match[] = { | ||
46 | { .compatible = "ti,pcm3168a", }, | ||
47 | { } | ||
48 | }; | ||
49 | MODULE_DEVICE_TABLE(of, pcm3168a_of_match); | ||
50 | |||
51 | static struct spi_driver pcm3168a_spi_driver = { | ||
52 | .probe = pcm3168a_spi_probe, | ||
53 | .remove = pcm3168a_spi_remove, | ||
54 | .id_table = pcm3168a_spi_id, | ||
55 | .driver = { | ||
56 | .name = "pcm3168a", | ||
57 | .of_match_table = pcm3168a_of_match, | ||
58 | .pm = &pcm3168a_pm_ops, | ||
59 | }, | ||
60 | }; | ||
61 | module_spi_driver(pcm3168a_spi_driver); | ||
62 | |||
63 | MODULE_DESCRIPTION("PCM3168A SPI codec driver"); | ||
64 | MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); | ||
65 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c new file mode 100644 index 000000000000..44b268aa4dd8 --- /dev/null +++ b/sound/soc/codecs/pcm3168a.c | |||
@@ -0,0 +1,767 @@ | |||
1 | /* | ||
2 | * PCM3168A codec driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * Author: Damien Horsley <Damien.Horsley@imgtec.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/pm_runtime.h> | ||
17 | #include <linux/regulator/consumer.h> | ||
18 | |||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/tlv.h> | ||
22 | |||
23 | #include "pcm3168a.h" | ||
24 | |||
25 | #define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ | ||
26 | SNDRV_PCM_FMTBIT_S24_3LE | \ | ||
27 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
28 | SNDRV_PCM_FMTBIT_S32_LE) | ||
29 | |||
30 | #define PCM3168A_FMT_I2S 0x0 | ||
31 | #define PCM3168A_FMT_LEFT_J 0x1 | ||
32 | #define PCM3168A_FMT_RIGHT_J 0x2 | ||
33 | #define PCM3168A_FMT_RIGHT_J_16 0x3 | ||
34 | #define PCM3168A_FMT_DSP_A 0x4 | ||
35 | #define PCM3168A_FMT_DSP_B 0x5 | ||
36 | #define PCM3168A_FMT_DSP_MASK 0x4 | ||
37 | |||
38 | #define PCM3168A_NUM_SUPPLIES 6 | ||
39 | static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { | ||
40 | "VDD1", | ||
41 | "VDD2", | ||
42 | "VCCAD1", | ||
43 | "VCCAD2", | ||
44 | "VCCDA1", | ||
45 | "VCCDA2" | ||
46 | }; | ||
47 | |||
48 | struct pcm3168a_priv { | ||
49 | struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES]; | ||
50 | struct regmap *regmap; | ||
51 | struct clk *scki; | ||
52 | bool adc_master_mode; | ||
53 | bool dac_master_mode; | ||
54 | unsigned long sysclk; | ||
55 | unsigned int adc_fmt; | ||
56 | unsigned int dac_fmt; | ||
57 | }; | ||
58 | |||
59 | static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" }; | ||
60 | |||
61 | static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT, | ||
62 | PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off); | ||
63 | static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT, | ||
64 | PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off); | ||
65 | static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT, | ||
66 | PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off); | ||
67 | static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT, | ||
68 | PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off); | ||
69 | |||
70 | static const char *const pcm3168a_volume_type[] = { | ||
71 | "Individual", "Master + Individual" }; | ||
72 | |||
73 | static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF, | ||
74 | PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type); | ||
75 | |||
76 | static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" }; | ||
77 | |||
78 | static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF, | ||
79 | PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult); | ||
80 | |||
81 | static const char *const pcm3168a_demp[] = { | ||
82 | "Disabled", "48khz", "44.1khz", "32khz" }; | ||
83 | |||
84 | static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF, | ||
85 | PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp); | ||
86 | |||
87 | static const char *const pcm3168a_zf_func[] = { | ||
88 | "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND", | ||
89 | "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" }; | ||
90 | |||
91 | static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF, | ||
92 | PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func); | ||
93 | |||
94 | static const char *const pcm3168a_pol[] = { "Active High", "Active Low" }; | ||
95 | |||
96 | static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF, | ||
97 | PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol); | ||
98 | |||
99 | static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" }; | ||
100 | |||
101 | static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD, | ||
102 | 0, 1, pcm3168a_con); | ||
103 | static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD, | ||
104 | 2, 3, pcm3168a_con); | ||
105 | static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD, | ||
106 | 4, 5, pcm3168a_con); | ||
107 | |||
108 | static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF, | ||
109 | PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type); | ||
110 | |||
111 | static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF, | ||
112 | PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult); | ||
113 | |||
114 | static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF, | ||
115 | PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol); | ||
116 | |||
117 | /* -100db to 0db, register values 0-54 cause mute */ | ||
118 | static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1); | ||
119 | |||
120 | /* -100db to 20db, register values 0-14 cause mute */ | ||
121 | static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1); | ||
122 | |||
123 | static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { | ||
124 | SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT, | ||
125 | PCM3168A_DAC_PSMDA_SHIFT, 1, 1), | ||
126 | SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off), | ||
127 | SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off), | ||
128 | SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off), | ||
129 | SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off), | ||
130 | SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0), | ||
131 | SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0), | ||
132 | SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0), | ||
133 | SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0), | ||
134 | SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0), | ||
135 | SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0), | ||
136 | SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0), | ||
137 | SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0), | ||
138 | SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type), | ||
139 | SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult), | ||
140 | SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp), | ||
141 | SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func), | ||
142 | SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol), | ||
143 | SOC_SINGLE_RANGE_TLV("Master Playback Volume", | ||
144 | PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0, | ||
145 | pcm3168a_dac_tlv), | ||
146 | SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume", | ||
147 | PCM3168A_DAC_VOL_CHAN_START, | ||
148 | PCM3168A_DAC_VOL_CHAN_START + 1, | ||
149 | 0, 54, 255, 0, pcm3168a_dac_tlv), | ||
150 | SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume", | ||
151 | PCM3168A_DAC_VOL_CHAN_START + 2, | ||
152 | PCM3168A_DAC_VOL_CHAN_START + 3, | ||
153 | 0, 54, 255, 0, pcm3168a_dac_tlv), | ||
154 | SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume", | ||
155 | PCM3168A_DAC_VOL_CHAN_START + 4, | ||
156 | PCM3168A_DAC_VOL_CHAN_START + 5, | ||
157 | 0, 54, 255, 0, pcm3168a_dac_tlv), | ||
158 | SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume", | ||
159 | PCM3168A_DAC_VOL_CHAN_START + 6, | ||
160 | PCM3168A_DAC_VOL_CHAN_START + 7, | ||
161 | 0, 54, 255, 0, pcm3168a_dac_tlv), | ||
162 | SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, | ||
163 | PCM3168A_ADC_BYP_SHIFT, 1, 1), | ||
164 | SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, | ||
165 | PCM3168A_ADC_BYP_SHIFT + 1, 1, 1), | ||
166 | SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, | ||
167 | PCM3168A_ADC_BYP_SHIFT + 2, 1, 1), | ||
168 | SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con), | ||
169 | SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con), | ||
170 | SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con), | ||
171 | SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0), | ||
172 | SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0), | ||
173 | SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0), | ||
174 | SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0), | ||
175 | SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0), | ||
176 | SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0), | ||
177 | SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0), | ||
178 | SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0), | ||
179 | SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0), | ||
180 | SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type), | ||
181 | SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult), | ||
182 | SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol), | ||
183 | SOC_SINGLE_RANGE_TLV("Master Capture Volume", | ||
184 | PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0, | ||
185 | pcm3168a_adc_tlv), | ||
186 | SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume", | ||
187 | PCM3168A_ADC_VOL_CHAN_START, | ||
188 | PCM3168A_ADC_VOL_CHAN_START + 1, | ||
189 | 0, 14, 255, 0, pcm3168a_adc_tlv), | ||
190 | SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume", | ||
191 | PCM3168A_ADC_VOL_CHAN_START + 2, | ||
192 | PCM3168A_ADC_VOL_CHAN_START + 3, | ||
193 | 0, 14, 255, 0, pcm3168a_adc_tlv), | ||
194 | SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume", | ||
195 | PCM3168A_ADC_VOL_CHAN_START + 4, | ||
196 | PCM3168A_ADC_VOL_CHAN_START + 5, | ||
197 | 0, 14, 255, 0, pcm3168a_adc_tlv) | ||
198 | }; | ||
199 | |||
200 | static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = { | ||
201 | SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT, | ||
202 | PCM3168A_DAC_OPEDA_SHIFT, 1), | ||
203 | SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT, | ||
204 | PCM3168A_DAC_OPEDA_SHIFT + 1, 1), | ||
205 | SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT, | ||
206 | PCM3168A_DAC_OPEDA_SHIFT + 2, 1), | ||
207 | SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT, | ||
208 | PCM3168A_DAC_OPEDA_SHIFT + 3, 1), | ||
209 | |||
210 | SND_SOC_DAPM_OUTPUT("AOUT1L"), | ||
211 | SND_SOC_DAPM_OUTPUT("AOUT1R"), | ||
212 | SND_SOC_DAPM_OUTPUT("AOUT2L"), | ||
213 | SND_SOC_DAPM_OUTPUT("AOUT2R"), | ||
214 | SND_SOC_DAPM_OUTPUT("AOUT3L"), | ||
215 | SND_SOC_DAPM_OUTPUT("AOUT3R"), | ||
216 | SND_SOC_DAPM_OUTPUT("AOUT4L"), | ||
217 | SND_SOC_DAPM_OUTPUT("AOUT4R"), | ||
218 | |||
219 | SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB, | ||
220 | PCM3168A_ADC_PSVAD_SHIFT, 1), | ||
221 | SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB, | ||
222 | PCM3168A_ADC_PSVAD_SHIFT + 1, 1), | ||
223 | SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB, | ||
224 | PCM3168A_ADC_PSVAD_SHIFT + 2, 1), | ||
225 | |||
226 | SND_SOC_DAPM_INPUT("AIN1L"), | ||
227 | SND_SOC_DAPM_INPUT("AIN1R"), | ||
228 | SND_SOC_DAPM_INPUT("AIN2L"), | ||
229 | SND_SOC_DAPM_INPUT("AIN2R"), | ||
230 | SND_SOC_DAPM_INPUT("AIN3L"), | ||
231 | SND_SOC_DAPM_INPUT("AIN3R") | ||
232 | }; | ||
233 | |||
234 | static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = { | ||
235 | /* Playback */ | ||
236 | { "AOUT1L", NULL, "DAC1" }, | ||
237 | { "AOUT1R", NULL, "DAC1" }, | ||
238 | |||
239 | { "AOUT2L", NULL, "DAC2" }, | ||
240 | { "AOUT2R", NULL, "DAC2" }, | ||
241 | |||
242 | { "AOUT3L", NULL, "DAC3" }, | ||
243 | { "AOUT3R", NULL, "DAC3" }, | ||
244 | |||
245 | { "AOUT4L", NULL, "DAC4" }, | ||
246 | { "AOUT4R", NULL, "DAC4" }, | ||
247 | |||
248 | /* Capture */ | ||
249 | { "ADC1", NULL, "AIN1L" }, | ||
250 | { "ADC1", NULL, "AIN1R" }, | ||
251 | |||
252 | { "ADC2", NULL, "AIN2L" }, | ||
253 | { "ADC2", NULL, "AIN2R" }, | ||
254 | |||
255 | { "ADC3", NULL, "AIN3L" }, | ||
256 | { "ADC3", NULL, "AIN3R" } | ||
257 | }; | ||
258 | |||
259 | static unsigned int pcm3168a_scki_ratios[] = { | ||
260 | 768, | ||
261 | 512, | ||
262 | 384, | ||
263 | 256, | ||
264 | 192, | ||
265 | 128 | ||
266 | }; | ||
267 | |||
268 | #define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios) | ||
269 | #define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2) | ||
270 | |||
271 | #define PCM1368A_MAX_SYSCLK 36864000 | ||
272 | |||
273 | static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a) | ||
274 | { | ||
275 | int ret; | ||
276 | |||
277 | ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0); | ||
278 | if (ret) | ||
279 | return ret; | ||
280 | |||
281 | /* Internal reset is de-asserted after 3846 SCKI cycles */ | ||
282 | msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk)); | ||
283 | |||
284 | return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, | ||
285 | PCM3168A_MRST_MASK | PCM3168A_SRST_MASK); | ||
286 | } | ||
287 | |||
288 | static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute) | ||
289 | { | ||
290 | struct snd_soc_codec *codec = dai->codec; | ||
291 | struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); | ||
292 | |||
293 | regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, | ||
299 | int clk_id, unsigned int freq, int dir) | ||
300 | { | ||
301 | struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec); | ||
302 | |||
303 | if (freq > PCM1368A_MAX_SYSCLK) | ||
304 | return -EINVAL; | ||
305 | |||
306 | pcm3168a->sysclk = freq; | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, | ||
312 | unsigned int format, bool dac) | ||
313 | { | ||
314 | struct snd_soc_codec *codec = dai->codec; | ||
315 | struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); | ||
316 | u32 fmt, reg, mask, shift; | ||
317 | bool master_mode; | ||
318 | |||
319 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
320 | case SND_SOC_DAIFMT_LEFT_J: | ||
321 | fmt = PCM3168A_FMT_LEFT_J; | ||
322 | break; | ||
323 | case SND_SOC_DAIFMT_I2S: | ||
324 | fmt = PCM3168A_FMT_I2S; | ||
325 | break; | ||
326 | case SND_SOC_DAIFMT_RIGHT_J: | ||
327 | fmt = PCM3168A_FMT_RIGHT_J; | ||
328 | break; | ||
329 | case SND_SOC_DAIFMT_DSP_A: | ||
330 | fmt = PCM3168A_FMT_DSP_A; | ||
331 | break; | ||
332 | case SND_SOC_DAIFMT_DSP_B: | ||
333 | fmt = PCM3168A_FMT_DSP_B; | ||
334 | break; | ||
335 | default: | ||
336 | dev_err(codec->dev, "unsupported dai format\n"); | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | |||
340 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { | ||
341 | case SND_SOC_DAIFMT_CBS_CFS: | ||
342 | master_mode = false; | ||
343 | break; | ||
344 | case SND_SOC_DAIFMT_CBM_CFM: | ||
345 | master_mode = true; | ||
346 | break; | ||
347 | default: | ||
348 | dev_err(codec->dev, "unsupported master/slave mode\n"); | ||
349 | return -EINVAL; | ||
350 | } | ||
351 | |||
352 | switch (format & SND_SOC_DAIFMT_INV_MASK) { | ||
353 | case SND_SOC_DAIFMT_NB_NF: | ||
354 | break; | ||
355 | default: | ||
356 | return -EINVAL; | ||
357 | } | ||
358 | |||
359 | if (dac) { | ||
360 | reg = PCM3168A_DAC_PWR_MST_FMT; | ||
361 | mask = PCM3168A_DAC_FMT_MASK; | ||
362 | shift = PCM3168A_DAC_FMT_SHIFT; | ||
363 | pcm3168a->dac_master_mode = master_mode; | ||
364 | pcm3168a->dac_fmt = fmt; | ||
365 | } else { | ||
366 | reg = PCM3168A_ADC_MST_FMT; | ||
367 | mask = PCM3168A_ADC_FMTAD_MASK; | ||
368 | shift = PCM3168A_ADC_FMTAD_SHIFT; | ||
369 | pcm3168a->adc_master_mode = master_mode; | ||
370 | pcm3168a->adc_fmt = fmt; | ||
371 | } | ||
372 | |||
373 | regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai, | ||
379 | unsigned int format) | ||
380 | { | ||
381 | return pcm3168a_set_dai_fmt(dai, format, true); | ||
382 | } | ||
383 | |||
384 | static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai, | ||
385 | unsigned int format) | ||
386 | { | ||
387 | return pcm3168a_set_dai_fmt(dai, format, false); | ||
388 | } | ||
389 | |||
390 | static int pcm3168a_hw_params(struct snd_pcm_substream *substream, | ||
391 | struct snd_pcm_hw_params *params, | ||
392 | struct snd_soc_dai *dai) | ||
393 | { | ||
394 | struct snd_soc_codec *codec = dai->codec; | ||
395 | struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); | ||
396 | bool tx, master_mode; | ||
397 | u32 val, mask, shift, reg; | ||
398 | unsigned int rate, channels, fmt, ratio, max_ratio; | ||
399 | int i, min_frame_size; | ||
400 | snd_pcm_format_t format; | ||
401 | |||
402 | rate = params_rate(params); | ||
403 | format = params_format(params); | ||
404 | channels = params_channels(params); | ||
405 | |||
406 | ratio = pcm3168a->sysclk / rate; | ||
407 | |||
408 | tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
409 | if (tx) { | ||
410 | max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC; | ||
411 | reg = PCM3168A_DAC_PWR_MST_FMT; | ||
412 | mask = PCM3168A_DAC_MSDA_MASK; | ||
413 | shift = PCM3168A_DAC_MSDA_SHIFT; | ||
414 | master_mode = pcm3168a->dac_master_mode; | ||
415 | fmt = pcm3168a->dac_fmt; | ||
416 | } else { | ||
417 | max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC; | ||
418 | reg = PCM3168A_ADC_MST_FMT; | ||
419 | mask = PCM3168A_ADC_MSAD_MASK; | ||
420 | shift = PCM3168A_ADC_MSAD_SHIFT; | ||
421 | master_mode = pcm3168a->adc_master_mode; | ||
422 | fmt = pcm3168a->adc_fmt; | ||
423 | } | ||
424 | |||
425 | for (i = 0; i < max_ratio; i++) { | ||
426 | if (pcm3168a_scki_ratios[i] == ratio) | ||
427 | break; | ||
428 | } | ||
429 | |||
430 | if (i == max_ratio) { | ||
431 | dev_err(codec->dev, "unsupported sysclk ratio\n"); | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | min_frame_size = params_width(params) * 2; | ||
436 | switch (min_frame_size) { | ||
437 | case 32: | ||
438 | if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) { | ||
439 | dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n"); | ||
440 | return -EINVAL; | ||
441 | } | ||
442 | fmt = PCM3168A_FMT_RIGHT_J_16; | ||
443 | break; | ||
444 | case 48: | ||
445 | if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) { | ||
446 | dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n"); | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | break; | ||
450 | case 64: | ||
451 | break; | ||
452 | default: | ||
453 | dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size); | ||
454 | return -EINVAL; | ||
455 | } | ||
456 | |||
457 | if (master_mode) | ||
458 | val = ((i + 1) << shift); | ||
459 | else | ||
460 | val = 0; | ||
461 | |||
462 | regmap_update_bits(pcm3168a->regmap, reg, mask, val); | ||
463 | |||
464 | if (tx) { | ||
465 | mask = PCM3168A_DAC_FMT_MASK; | ||
466 | shift = PCM3168A_DAC_FMT_SHIFT; | ||
467 | } else { | ||
468 | mask = PCM3168A_ADC_FMTAD_MASK; | ||
469 | shift = PCM3168A_ADC_FMTAD_SHIFT; | ||
470 | } | ||
471 | |||
472 | regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = { | ||
478 | .set_fmt = pcm3168a_set_dai_fmt_dac, | ||
479 | .set_sysclk = pcm3168a_set_dai_sysclk, | ||
480 | .hw_params = pcm3168a_hw_params, | ||
481 | .digital_mute = pcm3168a_digital_mute | ||
482 | }; | ||
483 | |||
484 | static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = { | ||
485 | .set_fmt = pcm3168a_set_dai_fmt_adc, | ||
486 | .set_sysclk = pcm3168a_set_dai_sysclk, | ||
487 | .hw_params = pcm3168a_hw_params | ||
488 | }; | ||
489 | |||
490 | static struct snd_soc_dai_driver pcm3168a_dais[] = { | ||
491 | { | ||
492 | .name = "pcm3168a-dac", | ||
493 | .playback = { | ||
494 | .stream_name = "Playback", | ||
495 | .channels_min = 1, | ||
496 | .channels_max = 8, | ||
497 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
498 | .formats = PCM3168A_FORMATS | ||
499 | }, | ||
500 | .ops = &pcm3168a_dac_dai_ops | ||
501 | }, | ||
502 | { | ||
503 | .name = "pcm3168a-adc", | ||
504 | .capture = { | ||
505 | .stream_name = "Capture", | ||
506 | .channels_min = 1, | ||
507 | .channels_max = 6, | ||
508 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
509 | .formats = PCM3168A_FORMATS | ||
510 | }, | ||
511 | .ops = &pcm3168a_adc_dai_ops | ||
512 | }, | ||
513 | }; | ||
514 | |||
515 | static const struct reg_default pcm3168a_reg_default[] = { | ||
516 | { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK }, | ||
517 | { PCM3168A_DAC_PWR_MST_FMT, 0x00 }, | ||
518 | { PCM3168A_DAC_OP_FLT, 0x00 }, | ||
519 | { PCM3168A_DAC_INV, 0x00 }, | ||
520 | { PCM3168A_DAC_MUTE, 0x00 }, | ||
521 | { PCM3168A_DAC_ZERO, 0x00 }, | ||
522 | { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 }, | ||
523 | { PCM3168A_DAC_VOL_MASTER, 0xff }, | ||
524 | { PCM3168A_DAC_VOL_CHAN_START, 0xff }, | ||
525 | { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff }, | ||
526 | { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff }, | ||
527 | { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff }, | ||
528 | { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff }, | ||
529 | { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff }, | ||
530 | { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff }, | ||
531 | { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff }, | ||
532 | { PCM3168A_ADC_SMODE, 0x00 }, | ||
533 | { PCM3168A_ADC_MST_FMT, 0x00 }, | ||
534 | { PCM3168A_ADC_PWR_HPFB, 0x00 }, | ||
535 | { PCM3168A_ADC_SEAD, 0x00 }, | ||
536 | { PCM3168A_ADC_INV, 0x00 }, | ||
537 | { PCM3168A_ADC_MUTE, 0x00 }, | ||
538 | { PCM3168A_ADC_OV, 0x00 }, | ||
539 | { PCM3168A_ADC_ATT_OVF, 0x00 }, | ||
540 | { PCM3168A_ADC_VOL_MASTER, 0xd3 }, | ||
541 | { PCM3168A_ADC_VOL_CHAN_START, 0xd3 }, | ||
542 | { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 }, | ||
543 | { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 }, | ||
544 | { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 }, | ||
545 | { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 }, | ||
546 | { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 } | ||
547 | }; | ||
548 | |||
549 | static bool pcm3168a_readable_register(struct device *dev, unsigned int reg) | ||
550 | { | ||
551 | if (reg >= PCM3168A_RST_SMODE) | ||
552 | return true; | ||
553 | else | ||
554 | return false; | ||
555 | } | ||
556 | |||
557 | static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg) | ||
558 | { | ||
559 | switch (reg) { | ||
560 | case PCM3168A_DAC_ZERO: | ||
561 | case PCM3168A_ADC_OV: | ||
562 | return true; | ||
563 | default: | ||
564 | return false; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg) | ||
569 | { | ||
570 | if (reg < PCM3168A_RST_SMODE) | ||
571 | return false; | ||
572 | |||
573 | switch (reg) { | ||
574 | case PCM3168A_DAC_ZERO: | ||
575 | case PCM3168A_ADC_OV: | ||
576 | return false; | ||
577 | default: | ||
578 | return true; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | const struct regmap_config pcm3168a_regmap = { | ||
583 | .reg_bits = 8, | ||
584 | .val_bits = 8, | ||
585 | |||
586 | .max_register = PCM3168A_ADC_VOL_CHAN_START + 5, | ||
587 | .reg_defaults = pcm3168a_reg_default, | ||
588 | .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default), | ||
589 | .readable_reg = pcm3168a_readable_register, | ||
590 | .volatile_reg = pcm3168a_volatile_register, | ||
591 | .writeable_reg = pcm3168a_writeable_register, | ||
592 | .cache_type = REGCACHE_FLAT | ||
593 | }; | ||
594 | EXPORT_SYMBOL_GPL(pcm3168a_regmap); | ||
595 | |||
596 | static const struct snd_soc_codec_driver pcm3168a_driver = { | ||
597 | .idle_bias_off = true, | ||
598 | .controls = pcm3168a_snd_controls, | ||
599 | .num_controls = ARRAY_SIZE(pcm3168a_snd_controls), | ||
600 | .dapm_widgets = pcm3168a_dapm_widgets, | ||
601 | .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets), | ||
602 | .dapm_routes = pcm3168a_dapm_routes, | ||
603 | .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes) | ||
604 | }; | ||
605 | |||
606 | int pcm3168a_probe(struct device *dev, struct regmap *regmap) | ||
607 | { | ||
608 | struct pcm3168a_priv *pcm3168a; | ||
609 | int ret, i; | ||
610 | |||
611 | pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL); | ||
612 | if (pcm3168a == NULL) | ||
613 | return -ENOMEM; | ||
614 | |||
615 | dev_set_drvdata(dev, pcm3168a); | ||
616 | |||
617 | pcm3168a->scki = devm_clk_get(dev, "scki"); | ||
618 | if (IS_ERR(pcm3168a->scki)) { | ||
619 | ret = PTR_ERR(pcm3168a->scki); | ||
620 | if (ret != -EPROBE_DEFER) | ||
621 | dev_err(dev, "failed to acquire clock 'scki': %d\n", ret); | ||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | ret = clk_prepare_enable(pcm3168a->scki); | ||
626 | if (ret) { | ||
627 | dev_err(dev, "Failed to enable mclk: %d\n", ret); | ||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | pcm3168a->sysclk = clk_get_rate(pcm3168a->scki); | ||
632 | |||
633 | for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++) | ||
634 | pcm3168a->supplies[i].supply = pcm3168a_supply_names[i]; | ||
635 | |||
636 | ret = devm_regulator_bulk_get(dev, | ||
637 | ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); | ||
638 | if (ret) { | ||
639 | if (ret != -EPROBE_DEFER) | ||
640 | dev_err(dev, "failed to request supplies: %d\n", ret); | ||
641 | goto err_clk; | ||
642 | } | ||
643 | |||
644 | ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), | ||
645 | pcm3168a->supplies); | ||
646 | if (ret) { | ||
647 | dev_err(dev, "failed to enable supplies: %d\n", ret); | ||
648 | goto err_clk; | ||
649 | } | ||
650 | |||
651 | pcm3168a->regmap = regmap; | ||
652 | if (IS_ERR(pcm3168a->regmap)) { | ||
653 | ret = PTR_ERR(pcm3168a->regmap); | ||
654 | dev_err(dev, "failed to allocate regmap: %d\n", ret); | ||
655 | goto err_regulator; | ||
656 | } | ||
657 | |||
658 | ret = pcm3168a_reset(pcm3168a); | ||
659 | if (ret) { | ||
660 | dev_err(dev, "Failed to reset device: %d\n", ret); | ||
661 | goto err_regulator; | ||
662 | } | ||
663 | |||
664 | pm_runtime_set_active(dev); | ||
665 | pm_runtime_enable(dev); | ||
666 | pm_runtime_idle(dev); | ||
667 | |||
668 | ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais, | ||
669 | ARRAY_SIZE(pcm3168a_dais)); | ||
670 | if (ret) { | ||
671 | dev_err(dev, "failed to register codec: %d\n", ret); | ||
672 | goto err_regulator; | ||
673 | } | ||
674 | |||
675 | return 0; | ||
676 | |||
677 | err_regulator: | ||
678 | regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), | ||
679 | pcm3168a->supplies); | ||
680 | err_clk: | ||
681 | clk_disable_unprepare(pcm3168a->scki); | ||
682 | |||
683 | return ret; | ||
684 | } | ||
685 | EXPORT_SYMBOL_GPL(pcm3168a_probe); | ||
686 | |||
687 | void pcm3168a_remove(struct device *dev) | ||
688 | { | ||
689 | struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); | ||
690 | |||
691 | snd_soc_unregister_codec(dev); | ||
692 | pm_runtime_disable(dev); | ||
693 | regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), | ||
694 | pcm3168a->supplies); | ||
695 | clk_disable_unprepare(pcm3168a->scki); | ||
696 | } | ||
697 | EXPORT_SYMBOL_GPL(pcm3168a_remove); | ||
698 | |||
699 | #ifdef CONFIG_PM | ||
700 | static int pcm3168a_rt_resume(struct device *dev) | ||
701 | { | ||
702 | struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); | ||
703 | int ret; | ||
704 | |||
705 | ret = clk_prepare_enable(pcm3168a->scki); | ||
706 | if (ret) { | ||
707 | dev_err(dev, "Failed to enable mclk: %d\n", ret); | ||
708 | return ret; | ||
709 | } | ||
710 | |||
711 | ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), | ||
712 | pcm3168a->supplies); | ||
713 | if (ret) { | ||
714 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | ||
715 | goto err_clk; | ||
716 | } | ||
717 | |||
718 | ret = pcm3168a_reset(pcm3168a); | ||
719 | if (ret) { | ||
720 | dev_err(dev, "Failed to reset device: %d\n", ret); | ||
721 | goto err_regulator; | ||
722 | } | ||
723 | |||
724 | regcache_cache_only(pcm3168a->regmap, false); | ||
725 | |||
726 | regcache_mark_dirty(pcm3168a->regmap); | ||
727 | |||
728 | ret = regcache_sync(pcm3168a->regmap); | ||
729 | if (ret) { | ||
730 | dev_err(dev, "Failed to sync regmap: %d\n", ret); | ||
731 | goto err_regulator; | ||
732 | } | ||
733 | |||
734 | return 0; | ||
735 | |||
736 | err_regulator: | ||
737 | regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), | ||
738 | pcm3168a->supplies); | ||
739 | err_clk: | ||
740 | clk_disable_unprepare(pcm3168a->scki); | ||
741 | |||
742 | return ret; | ||
743 | } | ||
744 | |||
745 | static int pcm3168a_rt_suspend(struct device *dev) | ||
746 | { | ||
747 | struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); | ||
748 | |||
749 | regcache_cache_only(pcm3168a->regmap, true); | ||
750 | |||
751 | regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), | ||
752 | pcm3168a->supplies); | ||
753 | |||
754 | clk_disable_unprepare(pcm3168a->scki); | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | #endif | ||
759 | |||
760 | const struct dev_pm_ops pcm3168a_pm_ops = { | ||
761 | SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) | ||
762 | }; | ||
763 | EXPORT_SYMBOL_GPL(pcm3168a_pm_ops); | ||
764 | |||
765 | MODULE_DESCRIPTION("PCM3168A codec driver"); | ||
766 | MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); | ||
767 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h new file mode 100644 index 000000000000..56c8332d82fb --- /dev/null +++ b/sound/soc/codecs/pcm3168a.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * PCM3168A codec driver header | ||
3 | * | ||
4 | * Copyright (C) 2015 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * Author: Damien Horsley <Damien.Horsley@imgtec.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __PCM3168A_H__ | ||
14 | #define __PCM3168A_H__ | ||
15 | |||
16 | extern const struct dev_pm_ops pcm3168a_pm_ops; | ||
17 | extern const struct regmap_config pcm3168a_regmap; | ||
18 | |||
19 | extern int pcm3168a_probe(struct device *dev, struct regmap *regmap); | ||
20 | extern void pcm3168a_remove(struct device *dev); | ||
21 | |||
22 | #define PCM3168A_RST_SMODE 0x40 | ||
23 | #define PCM3168A_MRST_MASK 0x80 | ||
24 | #define PCM3168A_SRST_MASK 0x40 | ||
25 | #define PCM3168A_DAC_SRDA_SHIFT 0 | ||
26 | #define PCM3168A_DAC_SRDA_MASK 0x3 | ||
27 | |||
28 | #define PCM3168A_DAC_PWR_MST_FMT 0x41 | ||
29 | #define PCM3168A_DAC_PSMDA_SHIFT 7 | ||
30 | #define PCM3168A_DAC_PSMDA_MASK 0x80 | ||
31 | #define PCM3168A_DAC_MSDA_SHIFT 4 | ||
32 | #define PCM3168A_DAC_MSDA_MASK 0x70 | ||
33 | #define PCM3168A_DAC_FMT_SHIFT 0 | ||
34 | #define PCM3168A_DAC_FMT_MASK 0xf | ||
35 | |||
36 | #define PCM3168A_DAC_OP_FLT 0x42 | ||
37 | #define PCM3168A_DAC_OPEDA_SHIFT 4 | ||
38 | #define PCM3168A_DAC_OPEDA_MASK 0xf0 | ||
39 | #define PCM3168A_DAC_FLT_SHIFT 0 | ||
40 | #define PCM3168A_DAC_FLT_MASK 0xf | ||
41 | |||
42 | #define PCM3168A_DAC_INV 0x43 | ||
43 | |||
44 | #define PCM3168A_DAC_MUTE 0x44 | ||
45 | |||
46 | #define PCM3168A_DAC_ZERO 0x45 | ||
47 | |||
48 | #define PCM3168A_DAC_ATT_DEMP_ZF 0x46 | ||
49 | #define PCM3168A_DAC_ATMDDA_MASK 0x80 | ||
50 | #define PCM3168A_DAC_ATMDDA_SHIFT 7 | ||
51 | #define PCM3168A_DAC_ATSPDA_MASK 0x40 | ||
52 | #define PCM3168A_DAC_ATSPDA_SHIFT 6 | ||
53 | #define PCM3168A_DAC_DEMP_SHIFT 4 | ||
54 | #define PCM3168A_DAC_DEMP_MASK 0x30 | ||
55 | #define PCM3168A_DAC_AZRO_SHIFT 1 | ||
56 | #define PCM3168A_DAC_AZRO_MASK 0xe | ||
57 | #define PCM3168A_DAC_ZREV_MASK 0x1 | ||
58 | #define PCM3168A_DAC_ZREV_SHIFT 0 | ||
59 | |||
60 | #define PCM3168A_DAC_VOL_MASTER 0x47 | ||
61 | |||
62 | #define PCM3168A_DAC_VOL_CHAN_START 0x48 | ||
63 | |||
64 | #define PCM3168A_ADC_SMODE 0x50 | ||
65 | #define PCM3168A_ADC_SRAD_SHIFT 0 | ||
66 | #define PCM3168A_ADC_SRAD_MASK 0x3 | ||
67 | |||
68 | #define PCM3168A_ADC_MST_FMT 0x51 | ||
69 | #define PCM3168A_ADC_MSAD_SHIFT 4 | ||
70 | #define PCM3168A_ADC_MSAD_MASK 0x70 | ||
71 | #define PCM3168A_ADC_FMTAD_SHIFT 0 | ||
72 | #define PCM3168A_ADC_FMTAD_MASK 0x7 | ||
73 | |||
74 | #define PCM3168A_ADC_PWR_HPFB 0x52 | ||
75 | #define PCM3168A_ADC_PSVAD_SHIFT 4 | ||
76 | #define PCM3168A_ADC_PSVAD_MASK 0x70 | ||
77 | #define PCM3168A_ADC_BYP_SHIFT 0 | ||
78 | #define PCM3168A_ADC_BYP_MASK 0x7 | ||
79 | |||
80 | #define PCM3168A_ADC_SEAD 0x53 | ||
81 | |||
82 | #define PCM3168A_ADC_INV 0x54 | ||
83 | |||
84 | #define PCM3168A_ADC_MUTE 0x55 | ||
85 | |||
86 | #define PCM3168A_ADC_OV 0x56 | ||
87 | |||
88 | #define PCM3168A_ADC_ATT_OVF 0x57 | ||
89 | #define PCM3168A_ADC_ATMDAD_MASK 0x80 | ||
90 | #define PCM3168A_ADC_ATMDAD_SHIFT 7 | ||
91 | #define PCM3168A_ADC_ATSPAD_MASK 0x40 | ||
92 | #define PCM3168A_ADC_ATSPAD_SHIFT 6 | ||
93 | #define PCM3168A_ADC_OVFP_MASK 0x1 | ||
94 | #define PCM3168A_ADC_OVFP_SHIFT 0 | ||
95 | |||
96 | #define PCM3168A_ADC_VOL_MASTER 0x58 | ||
97 | |||
98 | #define PCM3168A_ADC_VOL_CHAN_START 0x59 | ||
99 | |||
100 | #endif | ||