diff options
author | Mark Brown <broonie@kernel.org> | 2014-10-06 07:48:53 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-10-06 07:48:53 -0400 |
commit | 7ddb870b78e6f655b98231e23ae637e7d6fcf78f (patch) | |
tree | 09f8d9246a7e68715c012d0fc7bbd2ae429e1ac5 | |
parent | 64fdf13c8b5ff530f54dd767861f4e25a9ec2fe9 (diff) | |
parent | 5ea5570579739a8f80231d884e2979e25d3c0992 (diff) |
Merge remote-tracking branch 'asoc/topic/fsl-esai' into asoc-next
26 files changed, 2356 insertions, 42 deletions
diff --git a/Documentation/devicetree/bindings/regmap/regmap.txt b/Documentation/devicetree/bindings/regmap/regmap.txt new file mode 100644 index 000000000000..b494f8b8ef72 --- /dev/null +++ b/Documentation/devicetree/bindings/regmap/regmap.txt | |||
@@ -0,0 +1,47 @@ | |||
1 | Device-Tree binding for regmap | ||
2 | |||
3 | The endianness mode of CPU & Device scenarios: | ||
4 | Index Device Endianness properties | ||
5 | --------------------------------------------------- | ||
6 | 1 BE 'big-endian' | ||
7 | 2 LE 'little-endian' | ||
8 | |||
9 | For one device driver, which will run in different scenarios above | ||
10 | on different SoCs using the devicetree, we need one way to simplify | ||
11 | this. | ||
12 | |||
13 | Required properties: | ||
14 | - {big,little}-endian: these are boolean properties, if absent | ||
15 | meaning that the CPU and the Device are in the same endianness mode, | ||
16 | these properties are for register values and all the buffers only. | ||
17 | |||
18 | Examples: | ||
19 | Scenario 1 : CPU in LE mode & device in LE mode. | ||
20 | dev: dev@40031000 { | ||
21 | compatible = "name"; | ||
22 | reg = <0x40031000 0x1000>; | ||
23 | ... | ||
24 | }; | ||
25 | |||
26 | Scenario 2 : CPU in LE mode & device in BE mode. | ||
27 | dev: dev@40031000 { | ||
28 | compatible = "name"; | ||
29 | reg = <0x40031000 0x1000>; | ||
30 | ... | ||
31 | big-endian; | ||
32 | }; | ||
33 | |||
34 | Scenario 3 : CPU in BE mode & device in BE mode. | ||
35 | dev: dev@40031000 { | ||
36 | compatible = "name"; | ||
37 | reg = <0x40031000 0x1000>; | ||
38 | ... | ||
39 | }; | ||
40 | |||
41 | Scenario 4 : CPU in BE mode & device in LE mode. | ||
42 | dev: dev@40031000 { | ||
43 | compatible = "name"; | ||
44 | reg = <0x40031000 0x1000>; | ||
45 | ... | ||
46 | little-endian; | ||
47 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt new file mode 100644 index 000000000000..30ea8a318ae9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/es8328.txt | |||
@@ -0,0 +1,38 @@ | |||
1 | Everest ES8328 audio CODEC | ||
2 | |||
3 | This device supports both I2C and SPI. | ||
4 | |||
5 | Required properties: | ||
6 | |||
7 | - compatible : "everest,es8328" | ||
8 | - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V | ||
9 | - AVDD-supply : Regulator providing analog supply voltage 3.3V | ||
10 | - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V | ||
11 | - IPVDD-supply : Regulator providing analog output voltage 3.3V | ||
12 | - clocks : A 22.5792 or 11.2896 MHz clock | ||
13 | - reg : the I2C address of the device for I2C, the chip select number for SPI | ||
14 | |||
15 | Pins on the device (for linking into audio routes): | ||
16 | |||
17 | * LOUT1 | ||
18 | * LOUT2 | ||
19 | * ROUT1 | ||
20 | * ROUT2 | ||
21 | * LINPUT1 | ||
22 | * RINPUT1 | ||
23 | * LINPUT2 | ||
24 | * RINPUT2 | ||
25 | * Mic Bias | ||
26 | |||
27 | |||
28 | Example: | ||
29 | |||
30 | codec: es8328@11 { | ||
31 | compatible = "everest,es8328"; | ||
32 | DVDD-supply = <®_3p3v>; | ||
33 | AVDD-supply = <®_3p3v>; | ||
34 | PVDD-supply = <®_3p3v>; | ||
35 | HPVDD-supply = <®_3p3v>; | ||
36 | clocks = <&clks 169>; | ||
37 | reg = <0x11>; | ||
38 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/fsl,esai.txt b/Documentation/devicetree/bindings/sound/fsl,esai.txt index aeb8c4a0b88d..52f5b6bf3e8e 100644 --- a/Documentation/devicetree/bindings/sound/fsl,esai.txt +++ b/Documentation/devicetree/bindings/sound/fsl,esai.txt | |||
@@ -7,7 +7,8 @@ other DSPs. It has up to six transmitters and four receivers. | |||
7 | 7 | ||
8 | Required properties: | 8 | Required properties: |
9 | 9 | ||
10 | - compatible : Compatible list, must contain "fsl,imx35-esai". | 10 | - compatible : Compatible list, must contain "fsl,imx35-esai" or |
11 | "fsl,vf610-esai" | ||
11 | 12 | ||
12 | - reg : Offset and length of the register set for the device. | 13 | - reg : Offset and length of the register set for the device. |
13 | 14 | ||
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt new file mode 100644 index 000000000000..a96774c194c8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | |||
@@ -0,0 +1,82 @@ | |||
1 | Freescale Generic ASoC Sound Card with ASRC support | ||
2 | |||
3 | The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale | ||
4 | SoCs connecting with external CODECs. | ||
5 | |||
6 | The idea of this generic sound card is a bit like ASoC Simple Card. However, | ||
7 | for Freescale SoCs (especially those released in recent years), most of them | ||
8 | have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And | ||
9 | this is a specific feature that might be painstakingly controlled and merged | ||
10 | into the Simple Card. | ||
11 | |||
12 | So having this generic sound card allows all Freescale SoC users to benefit | ||
13 | from the simplification of a new card support and the capability of the wide | ||
14 | sample rates support through ASRC. | ||
15 | |||
16 | Note: The card is initially designed for those sound cards who use I2S and | ||
17 | PCM DAI formats. However, it'll be also possible to support those non | ||
18 | I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long | ||
19 | as the driver has been properly upgraded. | ||
20 | |||
21 | |||
22 | The compatible list for this generic sound card currently: | ||
23 | "fsl,imx-audio-cs42888" | ||
24 | |||
25 | "fsl,imx-audio-wm8962" | ||
26 | (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt) | ||
27 | |||
28 | "fsl,imx-audio-sgtl5000" | ||
29 | (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt) | ||
30 | |||
31 | Required properties: | ||
32 | |||
33 | - compatible : Contains one of entries in the compatible list. | ||
34 | |||
35 | - model : The user-visible name of this sound complex | ||
36 | |||
37 | - audio-cpu : The phandle of an CPU DAI controller | ||
38 | |||
39 | - audio-codec : The phandle of an audio codec | ||
40 | |||
41 | - audio-routing : A list of the connections between audio components. | ||
42 | Each entry is a pair of strings, the first being the | ||
43 | connection's sink, the second being the connection's | ||
44 | source. There're a few pre-designed board connectors: | ||
45 | * Line Out Jack | ||
46 | * Line In Jack | ||
47 | * Headphone Jack | ||
48 | * Mic Jack | ||
49 | * Ext Spk | ||
50 | * AMIC (stands for Analog Microphone Jack) | ||
51 | * DMIC (stands for Digital Microphone Jack) | ||
52 | |||
53 | Note: The "Mic Jack" and "AMIC" are redundant while | ||
54 | coexsiting in order to support the old bindings | ||
55 | of wm8962 and sgtl5000. | ||
56 | |||
57 | Optional properties: | ||
58 | |||
59 | - audio-asrc : The phandle of ASRC. It can be absent if there's no | ||
60 | need to add ASRC support via DPCM. | ||
61 | |||
62 | Example: | ||
63 | sound-cs42888 { | ||
64 | compatible = "fsl,imx-audio-cs42888"; | ||
65 | model = "cs42888-audio"; | ||
66 | audio-cpu = <&esai>; | ||
67 | audio-asrc = <&asrc>; | ||
68 | audio-codec = <&cs42888>; | ||
69 | audio-routing = | ||
70 | "Line Out Jack", "AOUT1L", | ||
71 | "Line Out Jack", "AOUT1R", | ||
72 | "Line Out Jack", "AOUT2L", | ||
73 | "Line Out Jack", "AOUT2R", | ||
74 | "Line Out Jack", "AOUT3L", | ||
75 | "Line Out Jack", "AOUT3R", | ||
76 | "Line Out Jack", "AOUT4L", | ||
77 | "Line Out Jack", "AOUT4R", | ||
78 | "AIN1L", "Line In Jack", | ||
79 | "AIN1R", "Line In Jack", | ||
80 | "AIN2L", "Line In Jack", | ||
81 | "AIN2R", "Line In Jack"; | ||
82 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 0f4e23828190..5f239b8bcddd 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt | |||
@@ -18,9 +18,8 @@ Required properties: | |||
18 | - pinctrl-names: Must contain a "default" entry. | 18 | - pinctrl-names: Must contain a "default" entry. |
19 | - pinctrl-NNN: One property must exist for each entry in pinctrl-names. | 19 | - pinctrl-NNN: One property must exist for each entry in pinctrl-names. |
20 | See ../pinctrl/pinctrl-bindings.txt for details of the property values. | 20 | See ../pinctrl/pinctrl-bindings.txt for details of the property values. |
21 | - big-endian-regs: If this property is absent, the little endian mode will | 21 | - big-endian: Boolean property, required if all the FTM_PWM registers |
22 | be in use as default, or the big endian mode will be in use for all the | 22 | are big-endian rather than little-endian. |
23 | device registers. | ||
24 | - big-endian-data: If this property is absent, the little endian mode will | 23 | - big-endian-data: If this property is absent, the little endian mode will |
25 | be in use as default, or the big endian mode will be in use for all the | 24 | be in use as default, or the big endian mode will be in use for all the |
26 | fifo data. | 25 | fifo data. |
@@ -38,6 +37,6 @@ sai2: sai@40031000 { | |||
38 | dma-names = "tx", "rx"; | 37 | dma-names = "tx", "rx"; |
39 | dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, | 38 | dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, |
40 | <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; | 39 | <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; |
41 | big-endian-regs; | 40 | big-endian; |
42 | big-endian-data; | 41 | big-endian-data; |
43 | }; | 42 | }; |
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt b/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt new file mode 100644 index 000000000000..07b68ab206fb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt | |||
@@ -0,0 +1,60 @@ | |||
1 | Freescale i.MX audio complex with ES8328 codec | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "fsl,imx-audio-es8328" | ||
5 | - model : The user-visible name of this sound complex | ||
6 | - ssi-controller : The phandle of the i.MX SSI controller | ||
7 | - jack-gpio : Optional GPIO for headphone jack | ||
8 | - audio-amp-supply : Power regulator for speaker amps | ||
9 | - audio-codec : The phandle of the ES8328 audio codec | ||
10 | - audio-routing : A list of the connections between audio components. | ||
11 | Each entry is a pair of strings, the first being the | ||
12 | connection's sink, the second being the connection's | ||
13 | source. Valid names could be power supplies, ES8328 | ||
14 | pins, and the jacks on the board: | ||
15 | |||
16 | Power supplies: | ||
17 | * audio-amp | ||
18 | |||
19 | ES8328 pins: | ||
20 | * LOUT1 | ||
21 | * LOUT2 | ||
22 | * ROUT1 | ||
23 | * ROUT2 | ||
24 | * LINPUT1 | ||
25 | * LINPUT2 | ||
26 | * RINPUT1 | ||
27 | * RINPUT2 | ||
28 | * Mic PGA | ||
29 | |||
30 | Board connectors: | ||
31 | * Headphone | ||
32 | * Speaker | ||
33 | * Mic Jack | ||
34 | - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) | ||
35 | - mux-ext-port : The external port of the i.MX audio muxer (AUDMIX) | ||
36 | |||
37 | Note: The AUDMUX port numbering should start at 1, which is consistent with | ||
38 | hardware manual. | ||
39 | |||
40 | Example: | ||
41 | |||
42 | sound { | ||
43 | compatible = "fsl,imx-audio-es8328"; | ||
44 | model = "imx-audio-es8328"; | ||
45 | ssi-controller = <&ssi1>; | ||
46 | audio-codec = <&codec>; | ||
47 | jack-gpio = <&gpio5 15 0>; | ||
48 | audio-amp-supply = <®_audio_amp>; | ||
49 | audio-routing = | ||
50 | "Speaker", "LOUT2", | ||
51 | "Speaker", "ROUT2", | ||
52 | "Speaker", "audio-amp", | ||
53 | "Headphone", "ROUT1", | ||
54 | "Headphone", "LOUT1", | ||
55 | "LINPUT1", "Mic Jack", | ||
56 | "RINPUT1", "Mic Jack", | ||
57 | "Mic Jack", "Mic Bias"; | ||
58 | mux-int-port = <1>; | ||
59 | mux-ext-port = <3>; | ||
60 | }; | ||
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ac7269f90764..34cc1bfcebfd 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt | |||
@@ -48,6 +48,7 @@ epfl Ecole Polytechnique Fédérale de Lausanne | |||
48 | epson Seiko Epson Corp. | 48 | epson Seiko Epson Corp. |
49 | est ESTeem Wireless Modems | 49 | est ESTeem Wireless Modems |
50 | eukrea Eukréa Electromatique | 50 | eukrea Eukréa Electromatique |
51 | everest Everest Semiconductor Co. Ltd. | ||
51 | excito Excito | 52 | excito Excito |
52 | fsl Freescale Semiconductor | 53 | fsl Freescale Semiconductor |
53 | GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. | 54 | GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. |
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index ca193d1ef47c..053150a7f9f2 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c | |||
@@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = { | |||
168 | .write = regmap_i2c_write, | 168 | .write = regmap_i2c_write, |
169 | .gather_write = regmap_i2c_gather_write, | 169 | .gather_write = regmap_i2c_gather_write, |
170 | .read = regmap_i2c_read, | 170 | .read = regmap_i2c_read, |
171 | .reg_format_endian_default = REGMAP_ENDIAN_BIG, | ||
172 | .val_format_endian_default = REGMAP_ENDIAN_BIG, | ||
171 | }; | 173 | }; |
172 | 174 | ||
173 | static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, | 175 | static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, |
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 0eb3097c0d76..53d1148e80a0 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c | |||
@@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = { | |||
109 | .async_alloc = regmap_spi_async_alloc, | 109 | .async_alloc = regmap_spi_async_alloc, |
110 | .read = regmap_spi_read, | 110 | .read = regmap_spi_read, |
111 | .read_flag_mask = 0x80, | 111 | .read_flag_mask = 0x80, |
112 | .reg_format_endian_default = REGMAP_ENDIAN_BIG, | ||
113 | .val_format_endian_default = REGMAP_ENDIAN_BIG, | ||
112 | }; | 114 | }; |
113 | 115 | ||
114 | /** | 116 | /** |
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 1cf427bc0d4a..f2281af24ec6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/export.h> | 15 | #include <linux/export.h> |
16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/of.h> | ||
18 | #include <linux/rbtree.h> | 19 | #include <linux/rbtree.h> |
19 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
20 | 21 | ||
@@ -448,6 +449,66 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, | |||
448 | } | 449 | } |
449 | EXPORT_SYMBOL_GPL(regmap_attach_dev); | 450 | EXPORT_SYMBOL_GPL(regmap_attach_dev); |
450 | 451 | ||
452 | static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus, | ||
453 | const struct regmap_config *config) | ||
454 | { | ||
455 | enum regmap_endian endian; | ||
456 | |||
457 | /* Retrieve the endianness specification from the regmap config */ | ||
458 | endian = config->reg_format_endian; | ||
459 | |||
460 | /* If the regmap config specified a non-default value, use that */ | ||
461 | if (endian != REGMAP_ENDIAN_DEFAULT) | ||
462 | return endian; | ||
463 | |||
464 | /* Retrieve the endianness specification from the bus config */ | ||
465 | if (bus && bus->reg_format_endian_default) | ||
466 | endian = bus->reg_format_endian_default; | ||
467 | |||
468 | /* If the bus specified a non-default value, use that */ | ||
469 | if (endian != REGMAP_ENDIAN_DEFAULT) | ||
470 | return endian; | ||
471 | |||
472 | /* Use this if no other value was found */ | ||
473 | return REGMAP_ENDIAN_BIG; | ||
474 | } | ||
475 | |||
476 | static enum regmap_endian regmap_get_val_endian(struct device *dev, | ||
477 | const struct regmap_bus *bus, | ||
478 | const struct regmap_config *config) | ||
479 | { | ||
480 | struct device_node *np = dev->of_node; | ||
481 | enum regmap_endian endian; | ||
482 | |||
483 | /* Retrieve the endianness specification from the regmap config */ | ||
484 | endian = config->val_format_endian; | ||
485 | |||
486 | /* If the regmap config specified a non-default value, use that */ | ||
487 | if (endian != REGMAP_ENDIAN_DEFAULT) | ||
488 | return endian; | ||
489 | |||
490 | /* Parse the device's DT node for an endianness specification */ | ||
491 | if (of_property_read_bool(np, "big-endian")) | ||
492 | endian = REGMAP_ENDIAN_BIG; | ||
493 | else if (of_property_read_bool(np, "little-endian")) | ||
494 | endian = REGMAP_ENDIAN_LITTLE; | ||
495 | |||
496 | /* If the endianness was specified in DT, use that */ | ||
497 | if (endian != REGMAP_ENDIAN_DEFAULT) | ||
498 | return endian; | ||
499 | |||
500 | /* Retrieve the endianness specification from the bus config */ | ||
501 | if (bus && bus->val_format_endian_default) | ||
502 | endian = bus->val_format_endian_default; | ||
503 | |||
504 | /* If the bus specified a non-default value, use that */ | ||
505 | if (endian != REGMAP_ENDIAN_DEFAULT) | ||
506 | return endian; | ||
507 | |||
508 | /* Use this if no other value was found */ | ||
509 | return REGMAP_ENDIAN_BIG; | ||
510 | } | ||
511 | |||
451 | /** | 512 | /** |
452 | * regmap_init(): Initialise register map | 513 | * regmap_init(): Initialise register map |
453 | * | 514 | * |
@@ -551,17 +612,8 @@ struct regmap *regmap_init(struct device *dev, | |||
551 | map->reg_read = _regmap_bus_read; | 612 | map->reg_read = _regmap_bus_read; |
552 | } | 613 | } |
553 | 614 | ||
554 | reg_endian = config->reg_format_endian; | 615 | reg_endian = regmap_get_reg_endian(bus, config); |
555 | if (reg_endian == REGMAP_ENDIAN_DEFAULT) | 616 | val_endian = regmap_get_val_endian(dev, bus, config); |
556 | reg_endian = bus->reg_format_endian_default; | ||
557 | if (reg_endian == REGMAP_ENDIAN_DEFAULT) | ||
558 | reg_endian = REGMAP_ENDIAN_BIG; | ||
559 | |||
560 | val_endian = config->val_format_endian; | ||
561 | if (val_endian == REGMAP_ENDIAN_DEFAULT) | ||
562 | val_endian = bus->val_format_endian_default; | ||
563 | if (val_endian == REGMAP_ENDIAN_DEFAULT) | ||
564 | val_endian = REGMAP_ENDIAN_BIG; | ||
565 | 617 | ||
566 | switch (config->reg_bits + map->reg_shift) { | 618 | switch (config->reg_bits + map->reg_shift) { |
567 | case 2: | 619 | case 2: |
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e25ed..8bca6343d8a3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -57,6 +57,8 @@ config SND_SOC_ALL_CODECS | |||
57 | select SND_SOC_DA732X if I2C | 57 | select SND_SOC_DA732X if I2C |
58 | select SND_SOC_DA9055 if I2C | 58 | select SND_SOC_DA9055 if I2C |
59 | select SND_SOC_BT_SCO | 59 | select SND_SOC_BT_SCO |
60 | select SND_SOC_ES8328_SPI if SPI_MASTER | ||
61 | select SND_SOC_ES8328_I2C if I2C | ||
60 | select SND_SOC_ISABELLE if I2C | 62 | select SND_SOC_ISABELLE if I2C |
61 | select SND_SOC_JZ4740_CODEC | 63 | select SND_SOC_JZ4740_CODEC |
62 | select SND_SOC_LM4857 if I2C | 64 | select SND_SOC_LM4857 if I2C |
@@ -405,6 +407,17 @@ config SND_SOC_DMIC | |||
405 | config SND_SOC_HDMI_CODEC | 407 | config SND_SOC_HDMI_CODEC |
406 | tristate "HDMI stub CODEC" | 408 | tristate "HDMI stub CODEC" |
407 | 409 | ||
410 | config SND_SOC_ES8328 | ||
411 | tristate "Everest Semi ES8328 CODEC" | ||
412 | |||
413 | config SND_SOC_ES8328_I2C | ||
414 | tristate | ||
415 | select SND_SOC_ES8328 | ||
416 | |||
417 | config SND_SOC_ES8328_SPI | ||
418 | tristate | ||
419 | select SND_SOC_ES8328 | ||
420 | |||
408 | config SND_SOC_ISABELLE | 421 | config SND_SOC_ISABELLE |
409 | tristate | 422 | tristate |
410 | 423 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f0c5be..31a8283006d1 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -49,6 +49,9 @@ snd-soc-da732x-objs := da732x.o | |||
49 | snd-soc-da9055-objs := da9055.o | 49 | snd-soc-da9055-objs := da9055.o |
50 | snd-soc-bt-sco-objs := bt-sco.o | 50 | snd-soc-bt-sco-objs := bt-sco.o |
51 | snd-soc-dmic-objs := dmic.o | 51 | snd-soc-dmic-objs := dmic.o |
52 | snd-soc-es8328-objs := es8328.o | ||
53 | snd-soc-es8328-i2c-objs := es8328-i2c.o | ||
54 | snd-soc-es8328-spi-objs := es8328-spi.o | ||
52 | snd-soc-isabelle-objs := isabelle.o | 55 | snd-soc-isabelle-objs := isabelle.o |
53 | snd-soc-jz4740-codec-objs := jz4740.o | 56 | snd-soc-jz4740-codec-objs := jz4740.o |
54 | snd-soc-l3-objs := l3.o | 57 | snd-soc-l3-objs := l3.o |
@@ -220,6 +223,9 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o | |||
220 | obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o | 223 | obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o |
221 | obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o | 224 | obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o |
222 | obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o | 225 | obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o |
226 | obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o | ||
227 | obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o | ||
228 | obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o | ||
223 | obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o | 229 | obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o |
224 | obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o | 230 | obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o |
225 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o | 231 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o |
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c new file mode 100644 index 000000000000..aae410d122ee --- /dev/null +++ b/sound/soc/codecs/es8328-i2c.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver | ||
3 | * | ||
4 | * Copyright 2014 Sutajio Ko-Usagi PTE LTD | ||
5 | * | ||
6 | * Author: Sean Cross <xobs@kosagi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/regmap.h> | ||
16 | |||
17 | #include <sound/soc.h> | ||
18 | |||
19 | #include "es8328.h" | ||
20 | |||
21 | static const struct i2c_device_id es8328_id[] = { | ||
22 | { "everest,es8328", 0 }, | ||
23 | { } | ||
24 | }; | ||
25 | MODULE_DEVICE_TABLE(i2c, es8328_id); | ||
26 | |||
27 | static const struct of_device_id es8328_of_match[] = { | ||
28 | { .compatible = "everest,es8328", }, | ||
29 | { } | ||
30 | }; | ||
31 | MODULE_DEVICE_TABLE(of, es8328_of_match); | ||
32 | |||
33 | static int es8328_i2c_probe(struct i2c_client *i2c, | ||
34 | const struct i2c_device_id *id) | ||
35 | { | ||
36 | return es8328_probe(&i2c->dev, | ||
37 | devm_regmap_init_i2c(i2c, &es8328_regmap_config)); | ||
38 | } | ||
39 | |||
40 | static int es8328_i2c_remove(struct i2c_client *i2c) | ||
41 | { | ||
42 | snd_soc_unregister_codec(&i2c->dev); | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static struct i2c_driver es8328_i2c_driver = { | ||
47 | .driver = { | ||
48 | .name = "es8328", | ||
49 | .of_match_table = es8328_of_match, | ||
50 | }, | ||
51 | .probe = es8328_i2c_probe, | ||
52 | .remove = es8328_i2c_remove, | ||
53 | .id_table = es8328_id, | ||
54 | }; | ||
55 | |||
56 | module_i2c_driver(es8328_i2c_driver); | ||
57 | |||
58 | MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver"); | ||
59 | MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
60 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c new file mode 100644 index 000000000000..8fbd935e1c76 --- /dev/null +++ b/sound/soc/codecs/es8328-spi.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * es8328.c -- ES8328 ALSA SoC SPI Audio driver | ||
3 | * | ||
4 | * Copyright 2014 Sutajio Ko-Usagi PTE LTD | ||
5 | * | ||
6 | * Author: Sean Cross <xobs@kosagi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/regmap.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <sound/soc.h> | ||
17 | #include "es8328.h" | ||
18 | |||
19 | static const struct of_device_id es8328_of_match[] = { | ||
20 | { .compatible = "everest,es8328", }, | ||
21 | { } | ||
22 | }; | ||
23 | MODULE_DEVICE_TABLE(of, es8328_of_match); | ||
24 | |||
25 | static int es8328_spi_probe(struct spi_device *spi) | ||
26 | { | ||
27 | return es8328_probe(&spi->dev, | ||
28 | devm_regmap_init_spi(spi, &es8328_regmap_config)); | ||
29 | } | ||
30 | |||
31 | static int es8328_spi_remove(struct spi_device *spi) | ||
32 | { | ||
33 | snd_soc_unregister_codec(&spi->dev); | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static struct spi_driver es8328_spi_driver = { | ||
38 | .driver = { | ||
39 | .name = "es8328", | ||
40 | .of_match_table = es8328_of_match, | ||
41 | }, | ||
42 | .probe = es8328_spi_probe, | ||
43 | .remove = es8328_spi_remove, | ||
44 | }; | ||
45 | |||
46 | module_spi_driver(es8328_spi_driver); | ||
47 | MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver"); | ||
48 | MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
49 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c new file mode 100644 index 000000000000..7a9f65ad183d --- /dev/null +++ b/sound/soc/codecs/es8328.c | |||
@@ -0,0 +1,756 @@ | |||
1 | /* | ||
2 | * es8328.c -- ES8328 ALSA SoC Audio driver | ||
3 | * | ||
4 | * Copyright 2014 Sutajio Ko-Usagi PTE LTD | ||
5 | * | ||
6 | * Author: Sean Cross <xobs@kosagi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/pm.h> | ||
18 | #include <linux/regmap.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/initval.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/tlv.h> | ||
27 | #include "es8328.h" | ||
28 | |||
29 | #define ES8328_SYSCLK_RATE_1X 11289600 | ||
30 | #define ES8328_SYSCLK_RATE_2X 22579200 | ||
31 | |||
32 | /* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ | ||
33 | static struct { | ||
34 | int rate; | ||
35 | u8 ratio; | ||
36 | } mclk_ratios[] = { | ||
37 | { 8000, 9 }, | ||
38 | {11025, 7 }, | ||
39 | {22050, 4 }, | ||
40 | {44100, 2 }, | ||
41 | }; | ||
42 | |||
43 | /* regulator supplies for sgtl5000, VDDD is an optional external supply */ | ||
44 | enum sgtl5000_regulator_supplies { | ||
45 | DVDD, | ||
46 | AVDD, | ||
47 | PVDD, | ||
48 | HPVDD, | ||
49 | ES8328_SUPPLY_NUM | ||
50 | }; | ||
51 | |||
52 | /* vddd is optional supply */ | ||
53 | static const char * const supply_names[ES8328_SUPPLY_NUM] = { | ||
54 | "DVDD", | ||
55 | "AVDD", | ||
56 | "PVDD", | ||
57 | "HPVDD", | ||
58 | }; | ||
59 | |||
60 | #define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ | ||
61 | SNDRV_PCM_RATE_22050 | \ | ||
62 | SNDRV_PCM_RATE_11025) | ||
63 | #define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) | ||
64 | |||
65 | struct es8328_priv { | ||
66 | struct regmap *regmap; | ||
67 | struct clk *clk; | ||
68 | int playback_fs; | ||
69 | bool deemph; | ||
70 | struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; | ||
71 | }; | ||
72 | |||
73 | /* | ||
74 | * ES8328 Controls | ||
75 | */ | ||
76 | |||
77 | static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert", | ||
78 | "L + R Invert"}; | ||
79 | static SOC_ENUM_SINGLE_DECL(adcpol, | ||
80 | ES8328_ADCCONTROL6, 6, adcpol_txt); | ||
81 | |||
82 | static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0); | ||
83 | static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0); | ||
84 | static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0); | ||
85 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); | ||
86 | static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0); | ||
87 | |||
88 | static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; | ||
89 | |||
90 | static int es8328_set_deemph(struct snd_soc_codec *codec) | ||
91 | { | ||
92 | struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); | ||
93 | int val, i, best; | ||
94 | |||
95 | /* | ||
96 | * If we're using deemphasis select the nearest available sample | ||
97 | * rate. | ||
98 | */ | ||
99 | if (es8328->deemph) { | ||
100 | best = 1; | ||
101 | for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { | ||
102 | if (abs(deemph_settings[i] - es8328->playback_fs) < | ||
103 | abs(deemph_settings[best] - es8328->playback_fs)) | ||
104 | best = i; | ||
105 | } | ||
106 | |||
107 | val = best << 1; | ||
108 | } else { | ||
109 | val = 0; | ||
110 | } | ||
111 | |||
112 | dev_dbg(codec->dev, "Set deemphasis %d\n", val); | ||
113 | |||
114 | return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val); | ||
115 | } | ||
116 | |||
117 | static int es8328_get_deemph(struct snd_kcontrol *kcontrol, | ||
118 | struct snd_ctl_elem_value *ucontrol) | ||
119 | { | ||
120 | struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); | ||
121 | struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); | ||
122 | |||
123 | ucontrol->value.enumerated.item[0] = es8328->deemph; | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int es8328_put_deemph(struct snd_kcontrol *kcontrol, | ||
128 | struct snd_ctl_elem_value *ucontrol) | ||
129 | { | ||
130 | struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); | ||
131 | struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); | ||
132 | int deemph = ucontrol->value.enumerated.item[0]; | ||
133 | int ret; | ||
134 | |||
135 | if (deemph > 1) | ||
136 | return -EINVAL; | ||
137 | |||
138 | ret = es8328_set_deemph(codec); | ||
139 | if (ret < 0) | ||
140 | return ret; | ||
141 | |||
142 | es8328->deemph = deemph; | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | |||
148 | |||
149 | static const struct snd_kcontrol_new es8328_snd_controls[] = { | ||
150 | SOC_DOUBLE_R_TLV("Capture Digital Volume", | ||
151 | ES8328_ADCCONTROL8, ES8328_ADCCONTROL9, | ||
152 | 0, 0xc0, 1, dac_adc_tlv), | ||
153 | SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0), | ||
154 | |||
155 | SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, | ||
156 | es8328_get_deemph, es8328_put_deemph), | ||
157 | |||
158 | SOC_ENUM("Capture Polarity", adcpol), | ||
159 | |||
160 | SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", | ||
161 | ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv), | ||
162 | SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", | ||
163 | ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv), | ||
164 | SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", | ||
165 | ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv), | ||
166 | SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", | ||
167 | ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv), | ||
168 | |||
169 | SOC_DOUBLE_R_TLV("PCM Volume", | ||
170 | ES8328_LDACVOL, ES8328_RDACVOL, | ||
171 | 0, ES8328_DACVOL_MAX, 1, dac_adc_tlv), | ||
172 | |||
173 | SOC_DOUBLE_R_TLV("Output 1 Playback Volume", | ||
174 | ES8328_LOUT1VOL, ES8328_ROUT1VOL, | ||
175 | 0, ES8328_OUT1VOL_MAX, 0, play_tlv), | ||
176 | |||
177 | SOC_DOUBLE_R_TLV("Output 2 Playback Volume", | ||
178 | ES8328_LOUT2VOL, ES8328_ROUT2VOL, | ||
179 | 0, ES8328_OUT2VOL_MAX, 0, play_tlv), | ||
180 | |||
181 | SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1, | ||
182 | 4, 0, 8, 0, mic_tlv), | ||
183 | }; | ||
184 | |||
185 | /* | ||
186 | * DAPM Controls | ||
187 | */ | ||
188 | |||
189 | static const char * const es8328_line_texts[] = { | ||
190 | "Line 1", "Line 2", "PGA", "Differential"}; | ||
191 | |||
192 | static const struct soc_enum es8328_lline_enum = | ||
193 | SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3, | ||
194 | ARRAY_SIZE(es8328_line_texts), | ||
195 | es8328_line_texts); | ||
196 | static const struct snd_kcontrol_new es8328_left_line_controls = | ||
197 | SOC_DAPM_ENUM("Route", es8328_lline_enum); | ||
198 | |||
199 | static const struct soc_enum es8328_rline_enum = | ||
200 | SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0, | ||
201 | ARRAY_SIZE(es8328_line_texts), | ||
202 | es8328_line_texts); | ||
203 | static const struct snd_kcontrol_new es8328_right_line_controls = | ||
204 | SOC_DAPM_ENUM("Route", es8328_lline_enum); | ||
205 | |||
206 | /* Left Mixer */ | ||
207 | static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { | ||
208 | SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0), | ||
209 | SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0), | ||
210 | SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0), | ||
211 | SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0), | ||
212 | }; | ||
213 | |||
214 | /* Right Mixer */ | ||
215 | static const struct snd_kcontrol_new es8328_right_mixer_controls[] = { | ||
216 | SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0), | ||
217 | SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0), | ||
218 | SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0), | ||
219 | SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0), | ||
220 | }; | ||
221 | |||
222 | static const char * const es8328_pga_sel[] = { | ||
223 | "Line 1", "Line 2", "Line 3", "Differential"}; | ||
224 | |||
225 | /* Left PGA Mux */ | ||
226 | static const struct soc_enum es8328_lpga_enum = | ||
227 | SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6, | ||
228 | ARRAY_SIZE(es8328_pga_sel), | ||
229 | es8328_pga_sel); | ||
230 | static const struct snd_kcontrol_new es8328_left_pga_controls = | ||
231 | SOC_DAPM_ENUM("Route", es8328_lpga_enum); | ||
232 | |||
233 | /* Right PGA Mux */ | ||
234 | static const struct soc_enum es8328_rpga_enum = | ||
235 | SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4, | ||
236 | ARRAY_SIZE(es8328_pga_sel), | ||
237 | es8328_pga_sel); | ||
238 | static const struct snd_kcontrol_new es8328_right_pga_controls = | ||
239 | SOC_DAPM_ENUM("Route", es8328_rpga_enum); | ||
240 | |||
241 | /* Differential Mux */ | ||
242 | static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"}; | ||
243 | static SOC_ENUM_SINGLE_DECL(diffmux, | ||
244 | ES8328_ADCCONTROL3, 7, es8328_diff_sel); | ||
245 | static const struct snd_kcontrol_new es8328_diffmux_controls = | ||
246 | SOC_DAPM_ENUM("Route", diffmux); | ||
247 | |||
248 | /* Mono ADC Mux */ | ||
249 | static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)", | ||
250 | "Mono (Right)", "Digital Mono"}; | ||
251 | static SOC_ENUM_SINGLE_DECL(monomux, | ||
252 | ES8328_ADCCONTROL3, 3, es8328_mono_mux); | ||
253 | static const struct snd_kcontrol_new es8328_monomux_controls = | ||
254 | SOC_DAPM_ENUM("Route", monomux); | ||
255 | |||
256 | static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = { | ||
257 | SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, | ||
258 | &es8328_diffmux_controls), | ||
259 | SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, | ||
260 | &es8328_monomux_controls), | ||
261 | SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, | ||
262 | &es8328_monomux_controls), | ||
263 | |||
264 | SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER, | ||
265 | ES8328_ADCPOWER_AINL_OFF, 1, | ||
266 | &es8328_left_pga_controls), | ||
267 | SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER, | ||
268 | ES8328_ADCPOWER_AINR_OFF, 1, | ||
269 | &es8328_right_pga_controls), | ||
270 | |||
271 | SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, | ||
272 | &es8328_left_line_controls), | ||
273 | SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, | ||
274 | &es8328_right_line_controls), | ||
275 | |||
276 | SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER, | ||
277 | ES8328_ADCPOWER_ADCR_OFF, 1), | ||
278 | SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER, | ||
279 | ES8328_ADCPOWER_ADCL_OFF, 1), | ||
280 | |||
281 | SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER, | ||
282 | ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0), | ||
283 | SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER, | ||
284 | ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0), | ||
285 | |||
286 | SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER, | ||
287 | ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0), | ||
288 | SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER, | ||
289 | ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0), | ||
290 | |||
291 | SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER, | ||
292 | ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0), | ||
293 | SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER, | ||
294 | ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0), | ||
295 | |||
296 | SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER, | ||
297 | ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0), | ||
298 | SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER, | ||
299 | ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0), | ||
300 | |||
301 | SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER, | ||
302 | ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0), | ||
303 | SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER, | ||
304 | ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0), | ||
305 | |||
306 | SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER, | ||
307 | ES8328_DACPOWER_RDAC_OFF, 1), | ||
308 | SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER, | ||
309 | ES8328_DACPOWER_LDAC_OFF, 1), | ||
310 | |||
311 | SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, | ||
312 | &es8328_left_mixer_controls[0], | ||
313 | ARRAY_SIZE(es8328_left_mixer_controls)), | ||
314 | SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, | ||
315 | &es8328_right_mixer_controls[0], | ||
316 | ARRAY_SIZE(es8328_right_mixer_controls)), | ||
317 | |||
318 | SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER, | ||
319 | ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0), | ||
320 | SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER, | ||
321 | ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0), | ||
322 | SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER, | ||
323 | ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0), | ||
324 | SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER, | ||
325 | ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0), | ||
326 | |||
327 | SND_SOC_DAPM_OUTPUT("LOUT1"), | ||
328 | SND_SOC_DAPM_OUTPUT("ROUT1"), | ||
329 | SND_SOC_DAPM_OUTPUT("LOUT2"), | ||
330 | SND_SOC_DAPM_OUTPUT("ROUT2"), | ||
331 | |||
332 | SND_SOC_DAPM_INPUT("LINPUT1"), | ||
333 | SND_SOC_DAPM_INPUT("LINPUT2"), | ||
334 | SND_SOC_DAPM_INPUT("RINPUT1"), | ||
335 | SND_SOC_DAPM_INPUT("RINPUT2"), | ||
336 | }; | ||
337 | |||
338 | static const struct snd_soc_dapm_route es8328_dapm_routes[] = { | ||
339 | |||
340 | { "Left Line Mux", "Line 1", "LINPUT1" }, | ||
341 | { "Left Line Mux", "Line 2", "LINPUT2" }, | ||
342 | { "Left Line Mux", "PGA", "Left PGA Mux" }, | ||
343 | { "Left Line Mux", "Differential", "Differential Mux" }, | ||
344 | |||
345 | { "Right Line Mux", "Line 1", "RINPUT1" }, | ||
346 | { "Right Line Mux", "Line 2", "RINPUT2" }, | ||
347 | { "Right Line Mux", "PGA", "Right PGA Mux" }, | ||
348 | { "Right Line Mux", "Differential", "Differential Mux" }, | ||
349 | |||
350 | { "Left PGA Mux", "Line 1", "LINPUT1" }, | ||
351 | { "Left PGA Mux", "Line 2", "LINPUT2" }, | ||
352 | { "Left PGA Mux", "Differential", "Differential Mux" }, | ||
353 | |||
354 | { "Right PGA Mux", "Line 1", "RINPUT1" }, | ||
355 | { "Right PGA Mux", "Line 2", "RINPUT2" }, | ||
356 | { "Right PGA Mux", "Differential", "Differential Mux" }, | ||
357 | |||
358 | { "Differential Mux", "Line 1", "LINPUT1" }, | ||
359 | { "Differential Mux", "Line 1", "RINPUT1" }, | ||
360 | { "Differential Mux", "Line 2", "LINPUT2" }, | ||
361 | { "Differential Mux", "Line 2", "RINPUT2" }, | ||
362 | |||
363 | { "Left ADC Mux", "Stereo", "Left PGA Mux" }, | ||
364 | { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, | ||
365 | { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, | ||
366 | |||
367 | { "Right ADC Mux", "Stereo", "Right PGA Mux" }, | ||
368 | { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, | ||
369 | { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, | ||
370 | |||
371 | { "Left ADC", NULL, "Left ADC Mux" }, | ||
372 | { "Right ADC", NULL, "Right ADC Mux" }, | ||
373 | |||
374 | { "ADC DIG", NULL, "ADC STM" }, | ||
375 | { "ADC DIG", NULL, "ADC Vref" }, | ||
376 | { "ADC DIG", NULL, "ADC DLL" }, | ||
377 | |||
378 | { "Left ADC", NULL, "ADC DIG" }, | ||
379 | { "Right ADC", NULL, "ADC DIG" }, | ||
380 | |||
381 | { "Mic Bias", NULL, "Mic Bias Gen" }, | ||
382 | |||
383 | { "Left Line Mux", "Line 1", "LINPUT1" }, | ||
384 | { "Left Line Mux", "Line 2", "LINPUT2" }, | ||
385 | { "Left Line Mux", "PGA", "Left PGA Mux" }, | ||
386 | { "Left Line Mux", "Differential", "Differential Mux" }, | ||
387 | |||
388 | { "Right Line Mux", "Line 1", "RINPUT1" }, | ||
389 | { "Right Line Mux", "Line 2", "RINPUT2" }, | ||
390 | { "Right Line Mux", "PGA", "Right PGA Mux" }, | ||
391 | { "Right Line Mux", "Differential", "Differential Mux" }, | ||
392 | |||
393 | { "Left Out 1", NULL, "Left DAC" }, | ||
394 | { "Right Out 1", NULL, "Right DAC" }, | ||
395 | { "Left Out 2", NULL, "Left DAC" }, | ||
396 | { "Right Out 2", NULL, "Right DAC" }, | ||
397 | |||
398 | { "Left Mixer", "Playback Switch", "Left DAC" }, | ||
399 | { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, | ||
400 | { "Left Mixer", "Right Playback Switch", "Right DAC" }, | ||
401 | { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, | ||
402 | |||
403 | { "Right Mixer", "Left Playback Switch", "Left DAC" }, | ||
404 | { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, | ||
405 | { "Right Mixer", "Playback Switch", "Right DAC" }, | ||
406 | { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, | ||
407 | |||
408 | { "DAC DIG", NULL, "DAC STM" }, | ||
409 | { "DAC DIG", NULL, "DAC Vref" }, | ||
410 | { "DAC DIG", NULL, "DAC DLL" }, | ||
411 | |||
412 | { "Left DAC", NULL, "DAC DIG" }, | ||
413 | { "Right DAC", NULL, "DAC DIG" }, | ||
414 | |||
415 | { "Left Out 1", NULL, "Left Mixer" }, | ||
416 | { "LOUT1", NULL, "Left Out 1" }, | ||
417 | { "Right Out 1", NULL, "Right Mixer" }, | ||
418 | { "ROUT1", NULL, "Right Out 1" }, | ||
419 | |||
420 | { "Left Out 2", NULL, "Left Mixer" }, | ||
421 | { "LOUT2", NULL, "Left Out 2" }, | ||
422 | { "Right Out 2", NULL, "Right Mixer" }, | ||
423 | { "ROUT2", NULL, "Right Out 2" }, | ||
424 | }; | ||
425 | |||
426 | static int es8328_mute(struct snd_soc_dai *dai, int mute) | ||
427 | { | ||
428 | return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3, | ||
429 | ES8328_DACCONTROL3_DACMUTE, | ||
430 | mute ? ES8328_DACCONTROL3_DACMUTE : 0); | ||
431 | } | ||
432 | |||
433 | static int es8328_hw_params(struct snd_pcm_substream *substream, | ||
434 | struct snd_pcm_hw_params *params, | ||
435 | struct snd_soc_dai *dai) | ||
436 | { | ||
437 | struct snd_soc_codec *codec = dai->codec; | ||
438 | struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); | ||
439 | int clk_rate; | ||
440 | int i; | ||
441 | int reg; | ||
442 | u8 ratio; | ||
443 | |||
444 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
445 | reg = ES8328_DACCONTROL2; | ||
446 | else | ||
447 | reg = ES8328_ADCCONTROL5; | ||
448 | |||
449 | clk_rate = clk_get_rate(es8328->clk); | ||
450 | |||
451 | if ((clk_rate != ES8328_SYSCLK_RATE_1X) && | ||
452 | (clk_rate != ES8328_SYSCLK_RATE_2X)) { | ||
453 | dev_err(codec->dev, | ||
454 | "%s: clock is running at %d Hz, not %d or %d Hz\n", | ||
455 | __func__, clk_rate, | ||
456 | ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); | ||
457 | return -EINVAL; | ||
458 | } | ||
459 | |||
460 | /* find master mode MCLK to sampling frequency ratio */ | ||
461 | ratio = mclk_ratios[0].rate; | ||
462 | for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) | ||
463 | if (params_rate(params) <= mclk_ratios[i].rate) | ||
464 | ratio = mclk_ratios[i].ratio; | ||
465 | |||
466 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
467 | es8328->playback_fs = params_rate(params); | ||
468 | es8328_set_deemph(codec); | ||
469 | } | ||
470 | |||
471 | return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); | ||
472 | } | ||
473 | |||
474 | static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
475 | unsigned int fmt) | ||
476 | { | ||
477 | struct snd_soc_codec *codec = codec_dai->codec; | ||
478 | struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); | ||
479 | int clk_rate; | ||
480 | u8 mode = ES8328_DACCONTROL1_DACWL_16; | ||
481 | |||
482 | /* set master/slave audio interface */ | ||
483 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) | ||
484 | return -EINVAL; | ||
485 | |||
486 | /* interface format */ | ||
487 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
488 | case SND_SOC_DAIFMT_I2S: | ||
489 | mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; | ||
490 | break; | ||
491 | case SND_SOC_DAIFMT_RIGHT_J: | ||
492 | mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; | ||
493 | break; | ||
494 | case SND_SOC_DAIFMT_LEFT_J: | ||
495 | mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; | ||
496 | break; | ||
497 | default: | ||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
501 | /* clock inversion */ | ||
502 | if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) | ||
503 | return -EINVAL; | ||
504 | |||
505 | snd_soc_write(codec, ES8328_DACCONTROL1, mode); | ||
506 | snd_soc_write(codec, ES8328_ADCCONTROL4, mode); | ||
507 | |||
508 | /* Master serial port mode, with BCLK generated automatically */ | ||
509 | clk_rate = clk_get_rate(es8328->clk); | ||
510 | if (clk_rate == ES8328_SYSCLK_RATE_1X) | ||
511 | snd_soc_write(codec, ES8328_MASTERMODE, | ||
512 | ES8328_MASTERMODE_MSC); | ||
513 | else | ||
514 | snd_soc_write(codec, ES8328_MASTERMODE, | ||
515 | ES8328_MASTERMODE_MCLKDIV2 | | ||
516 | ES8328_MASTERMODE_MSC); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int es8328_set_bias_level(struct snd_soc_codec *codec, | ||
522 | enum snd_soc_bias_level level) | ||
523 | { | ||
524 | switch (level) { | ||
525 | case SND_SOC_BIAS_ON: | ||
526 | break; | ||
527 | |||
528 | case SND_SOC_BIAS_PREPARE: | ||
529 | /* VREF, VMID=2x50k, digital enabled */ | ||
530 | snd_soc_write(codec, ES8328_CHIPPOWER, 0); | ||
531 | snd_soc_update_bits(codec, ES8328_CONTROL1, | ||
532 | ES8328_CONTROL1_VMIDSEL_MASK | | ||
533 | ES8328_CONTROL1_ENREF, | ||
534 | ES8328_CONTROL1_VMIDSEL_50k | | ||
535 | ES8328_CONTROL1_ENREF); | ||
536 | break; | ||
537 | |||
538 | case SND_SOC_BIAS_STANDBY: | ||
539 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
540 | snd_soc_update_bits(codec, ES8328_CONTROL1, | ||
541 | ES8328_CONTROL1_VMIDSEL_MASK | | ||
542 | ES8328_CONTROL1_ENREF, | ||
543 | ES8328_CONTROL1_VMIDSEL_5k | | ||
544 | ES8328_CONTROL1_ENREF); | ||
545 | |||
546 | /* Charge caps */ | ||
547 | msleep(100); | ||
548 | } | ||
549 | |||
550 | snd_soc_write(codec, ES8328_CONTROL2, | ||
551 | ES8328_CONTROL2_OVERCURRENT_ON | | ||
552 | ES8328_CONTROL2_THERMAL_SHUTDOWN_ON); | ||
553 | |||
554 | /* VREF, VMID=2*500k, digital stopped */ | ||
555 | snd_soc_update_bits(codec, ES8328_CONTROL1, | ||
556 | ES8328_CONTROL1_VMIDSEL_MASK | | ||
557 | ES8328_CONTROL1_ENREF, | ||
558 | ES8328_CONTROL1_VMIDSEL_500k | | ||
559 | ES8328_CONTROL1_ENREF); | ||
560 | break; | ||
561 | |||
562 | case SND_SOC_BIAS_OFF: | ||
563 | snd_soc_update_bits(codec, ES8328_CONTROL1, | ||
564 | ES8328_CONTROL1_VMIDSEL_MASK | | ||
565 | ES8328_CONTROL1_ENREF, | ||
566 | 0); | ||
567 | break; | ||
568 | } | ||
569 | codec->dapm.bias_level = level; | ||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static const struct snd_soc_dai_ops es8328_dai_ops = { | ||
574 | .hw_params = es8328_hw_params, | ||
575 | .digital_mute = es8328_mute, | ||
576 | .set_fmt = es8328_set_dai_fmt, | ||
577 | }; | ||
578 | |||
579 | static struct snd_soc_dai_driver es8328_dai = { | ||
580 | .name = "es8328-hifi-analog", | ||
581 | .playback = { | ||
582 | .stream_name = "Playback", | ||
583 | .channels_min = 2, | ||
584 | .channels_max = 2, | ||
585 | .rates = ES8328_RATES, | ||
586 | .formats = ES8328_FORMATS, | ||
587 | }, | ||
588 | .capture = { | ||
589 | .stream_name = "Capture", | ||
590 | .channels_min = 2, | ||
591 | .channels_max = 2, | ||
592 | .rates = ES8328_RATES, | ||
593 | .formats = ES8328_FORMATS, | ||
594 | }, | ||
595 | .ops = &es8328_dai_ops, | ||
596 | }; | ||
597 | |||
598 | static int es8328_suspend(struct snd_soc_codec *codec) | ||
599 | { | ||
600 | struct es8328_priv *es8328; | ||
601 | int ret; | ||
602 | |||
603 | es8328 = snd_soc_codec_get_drvdata(codec); | ||
604 | |||
605 | es8328_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
606 | |||
607 | clk_disable_unprepare(es8328->clk); | ||
608 | |||
609 | ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), | ||
610 | es8328->supplies); | ||
611 | if (ret) { | ||
612 | dev_err(codec->dev, "unable to disable regulators\n"); | ||
613 | return ret; | ||
614 | } | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int es8328_resume(struct snd_soc_codec *codec) | ||
619 | { | ||
620 | struct regmap *regmap = dev_get_regmap(codec->dev, NULL); | ||
621 | struct es8328_priv *es8328; | ||
622 | int ret; | ||
623 | |||
624 | es8328 = snd_soc_codec_get_drvdata(codec); | ||
625 | |||
626 | ret = clk_prepare_enable(es8328->clk); | ||
627 | if (ret) { | ||
628 | dev_err(codec->dev, "unable to enable clock\n"); | ||
629 | return ret; | ||
630 | } | ||
631 | |||
632 | ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), | ||
633 | es8328->supplies); | ||
634 | if (ret) { | ||
635 | dev_err(codec->dev, "unable to enable regulators\n"); | ||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | regcache_mark_dirty(regmap); | ||
640 | ret = regcache_sync(regmap); | ||
641 | if (ret) { | ||
642 | dev_err(codec->dev, "unable to sync regcache\n"); | ||
643 | return ret; | ||
644 | } | ||
645 | |||
646 | es8328_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | static int es8328_codec_probe(struct snd_soc_codec *codec) | ||
651 | { | ||
652 | struct es8328_priv *es8328; | ||
653 | int ret; | ||
654 | |||
655 | es8328 = snd_soc_codec_get_drvdata(codec); | ||
656 | |||
657 | ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), | ||
658 | es8328->supplies); | ||
659 | if (ret) { | ||
660 | dev_err(codec->dev, "unable to enable regulators\n"); | ||
661 | return ret; | ||
662 | } | ||
663 | |||
664 | /* Setup clocks */ | ||
665 | es8328->clk = devm_clk_get(codec->dev, NULL); | ||
666 | if (IS_ERR(es8328->clk)) { | ||
667 | dev_err(codec->dev, "codec clock missing or invalid\n"); | ||
668 | goto clk_fail; | ||
669 | } | ||
670 | |||
671 | ret = clk_prepare_enable(es8328->clk); | ||
672 | if (ret) { | ||
673 | dev_err(codec->dev, "unable to prepare codec clk\n"); | ||
674 | goto clk_fail; | ||
675 | } | ||
676 | |||
677 | return 0; | ||
678 | |||
679 | clk_fail: | ||
680 | regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), | ||
681 | es8328->supplies); | ||
682 | return ret; | ||
683 | } | ||
684 | |||
685 | static int es8328_remove(struct snd_soc_codec *codec) | ||
686 | { | ||
687 | struct es8328_priv *es8328; | ||
688 | |||
689 | es8328 = snd_soc_codec_get_drvdata(codec); | ||
690 | |||
691 | if (es8328->clk) | ||
692 | clk_disable_unprepare(es8328->clk); | ||
693 | |||
694 | regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), | ||
695 | es8328->supplies); | ||
696 | |||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | const struct regmap_config es8328_regmap_config = { | ||
701 | .reg_bits = 8, | ||
702 | .val_bits = 8, | ||
703 | .max_register = ES8328_REG_MAX, | ||
704 | .cache_type = REGCACHE_RBTREE, | ||
705 | }; | ||
706 | EXPORT_SYMBOL_GPL(es8328_regmap_config); | ||
707 | |||
708 | static struct snd_soc_codec_driver es8328_codec_driver = { | ||
709 | .probe = es8328_codec_probe, | ||
710 | .suspend = es8328_suspend, | ||
711 | .resume = es8328_resume, | ||
712 | .remove = es8328_remove, | ||
713 | .set_bias_level = es8328_set_bias_level, | ||
714 | .controls = es8328_snd_controls, | ||
715 | .num_controls = ARRAY_SIZE(es8328_snd_controls), | ||
716 | .dapm_widgets = es8328_dapm_widgets, | ||
717 | .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets), | ||
718 | .dapm_routes = es8328_dapm_routes, | ||
719 | .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes), | ||
720 | }; | ||
721 | |||
722 | int es8328_probe(struct device *dev, struct regmap *regmap) | ||
723 | { | ||
724 | struct es8328_priv *es8328; | ||
725 | int ret; | ||
726 | int i; | ||
727 | |||
728 | if (IS_ERR(regmap)) | ||
729 | return PTR_ERR(regmap); | ||
730 | |||
731 | es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL); | ||
732 | if (es8328 == NULL) | ||
733 | return -ENOMEM; | ||
734 | |||
735 | es8328->regmap = regmap; | ||
736 | |||
737 | for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++) | ||
738 | es8328->supplies[i].supply = supply_names[i]; | ||
739 | |||
740 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies), | ||
741 | es8328->supplies); | ||
742 | if (ret) { | ||
743 | dev_err(dev, "unable to get regulators\n"); | ||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | dev_set_drvdata(dev, es8328); | ||
748 | |||
749 | return snd_soc_register_codec(dev, | ||
750 | &es8328_codec_driver, &es8328_dai, 1); | ||
751 | } | ||
752 | EXPORT_SYMBOL_GPL(es8328_probe); | ||
753 | |||
754 | MODULE_DESCRIPTION("ASoC ES8328 driver"); | ||
755 | MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
756 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h new file mode 100644 index 000000000000..cb36afe10c0e --- /dev/null +++ b/sound/soc/codecs/es8328.h | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * es8328.h -- ES8328 ALSA SoC Audio driver | ||
3 | */ | ||
4 | |||
5 | #ifndef _ES8328_H | ||
6 | #define _ES8328_H | ||
7 | |||
8 | #include <linux/regmap.h> | ||
9 | |||
10 | struct device; | ||
11 | |||
12 | extern const struct regmap_config es8328_regmap_config; | ||
13 | int es8328_probe(struct device *dev, struct regmap *regmap); | ||
14 | |||
15 | #define ES8328_DACLVOL 46 | ||
16 | #define ES8328_DACRVOL 47 | ||
17 | #define ES8328_DACCTL 28 | ||
18 | #define ES8328_RATEMASK (0x1f << 0) | ||
19 | |||
20 | #define ES8328_CONTROL1 0x00 | ||
21 | #define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0) | ||
22 | #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) | ||
23 | #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) | ||
24 | #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) | ||
25 | #define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) | ||
26 | #define ES8328_CONTROL1_ENREF (1 << 2) | ||
27 | #define ES8328_CONTROL1_SEQEN (1 << 3) | ||
28 | #define ES8328_CONTROL1_SAMEFS (1 << 4) | ||
29 | #define ES8328_CONTROL1_DACMCLK_ADC (0 << 5) | ||
30 | #define ES8328_CONTROL1_DACMCLK_DAC (1 << 5) | ||
31 | #define ES8328_CONTROL1_LRCM (1 << 6) | ||
32 | #define ES8328_CONTROL1_SCP_RESET (1 << 7) | ||
33 | |||
34 | #define ES8328_CONTROL2 0x01 | ||
35 | #define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0) | ||
36 | #define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1) | ||
37 | #define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2) | ||
38 | #define ES8328_CONTROL2_ANALOG_OFF (1 << 3) | ||
39 | #define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4) | ||
40 | #define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5) | ||
41 | #define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6) | ||
42 | #define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7) | ||
43 | |||
44 | #define ES8328_CHIPPOWER 0x02 | ||
45 | #define ES8328_CHIPPOWER_DACVREF_OFF 0 | ||
46 | #define ES8328_CHIPPOWER_ADCVREF_OFF 1 | ||
47 | #define ES8328_CHIPPOWER_DACDLL_OFF 2 | ||
48 | #define ES8328_CHIPPOWER_ADCDLL_OFF 3 | ||
49 | #define ES8328_CHIPPOWER_DACSTM_RESET 4 | ||
50 | #define ES8328_CHIPPOWER_ADCSTM_RESET 5 | ||
51 | #define ES8328_CHIPPOWER_DACDIG_OFF 6 | ||
52 | #define ES8328_CHIPPOWER_ADCDIG_OFF 7 | ||
53 | |||
54 | #define ES8328_ADCPOWER 0x03 | ||
55 | #define ES8328_ADCPOWER_INT1_LOWPOWER 0 | ||
56 | #define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1 | ||
57 | #define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2 | ||
58 | #define ES8328_ADCPOWER_MIC_BIAS_OFF 3 | ||
59 | #define ES8328_ADCPOWER_ADCR_OFF 4 | ||
60 | #define ES8328_ADCPOWER_ADCL_OFF 5 | ||
61 | #define ES8328_ADCPOWER_AINR_OFF 6 | ||
62 | #define ES8328_ADCPOWER_AINL_OFF 7 | ||
63 | |||
64 | #define ES8328_DACPOWER 0x04 | ||
65 | #define ES8328_DACPOWER_OUT3_ON 0 | ||
66 | #define ES8328_DACPOWER_MONO_ON 1 | ||
67 | #define ES8328_DACPOWER_ROUT2_ON 2 | ||
68 | #define ES8328_DACPOWER_LOUT2_ON 3 | ||
69 | #define ES8328_DACPOWER_ROUT1_ON 4 | ||
70 | #define ES8328_DACPOWER_LOUT1_ON 5 | ||
71 | #define ES8328_DACPOWER_RDAC_OFF 6 | ||
72 | #define ES8328_DACPOWER_LDAC_OFF 7 | ||
73 | |||
74 | #define ES8328_CHIPLOPOW1 0x05 | ||
75 | #define ES8328_CHIPLOPOW2 0x06 | ||
76 | #define ES8328_ANAVOLMANAG 0x07 | ||
77 | |||
78 | #define ES8328_MASTERMODE 0x08 | ||
79 | #define ES8328_MASTERMODE_BCLKDIV (0 << 0) | ||
80 | #define ES8328_MASTERMODE_BCLK_INV (1 << 5) | ||
81 | #define ES8328_MASTERMODE_MCLKDIV2 (1 << 6) | ||
82 | #define ES8328_MASTERMODE_MSC (1 << 7) | ||
83 | |||
84 | #define ES8328_ADCCONTROL1 0x09 | ||
85 | #define ES8328_ADCCONTROL2 0x0a | ||
86 | #define ES8328_ADCCONTROL3 0x0b | ||
87 | #define ES8328_ADCCONTROL4 0x0c | ||
88 | #define ES8328_ADCCONTROL5 0x0d | ||
89 | #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) | ||
90 | |||
91 | #define ES8328_ADCCONTROL6 0x0e | ||
92 | |||
93 | #define ES8328_ADCCONTROL7 0x0f | ||
94 | #define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2) | ||
95 | #define ES8328_ADCCONTROL7_ADC_LER (1 << 3) | ||
96 | #define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4) | ||
97 | #define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5) | ||
98 | #define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6) | ||
99 | #define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6) | ||
100 | #define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6) | ||
101 | #define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6) | ||
102 | |||
103 | #define ES8328_ADCCONTROL8 0x10 | ||
104 | #define ES8328_ADCCONTROL9 0x11 | ||
105 | #define ES8328_ADCCONTROL10 0x12 | ||
106 | #define ES8328_ADCCONTROL11 0x13 | ||
107 | #define ES8328_ADCCONTROL12 0x14 | ||
108 | #define ES8328_ADCCONTROL13 0x15 | ||
109 | #define ES8328_ADCCONTROL14 0x16 | ||
110 | |||
111 | #define ES8328_DACCONTROL1 0x17 | ||
112 | #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) | ||
113 | #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) | ||
114 | #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) | ||
115 | #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) | ||
116 | #define ES8328_DACCONTROL1_DACWL_24 (0 << 3) | ||
117 | #define ES8328_DACCONTROL1_DACWL_20 (1 << 3) | ||
118 | #define ES8328_DACCONTROL1_DACWL_18 (2 << 3) | ||
119 | #define ES8328_DACCONTROL1_DACWL_16 (3 << 3) | ||
120 | #define ES8328_DACCONTROL1_DACWL_32 (4 << 3) | ||
121 | #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) | ||
122 | #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) | ||
123 | #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) | ||
124 | #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6) | ||
125 | #define ES8328_DACCONTROL1_LRSWAP (1 << 7) | ||
126 | |||
127 | #define ES8328_DACCONTROL2 0x18 | ||
128 | #define ES8328_DACCONTROL2_RATEMASK (0x1f << 0) | ||
129 | #define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5) | ||
130 | |||
131 | #define ES8328_DACCONTROL3 0x19 | ||
132 | #define ES8328_DACCONTROL3_AUTOMUTE (1 << 2) | ||
133 | #define ES8328_DACCONTROL3_DACMUTE (1 << 2) | ||
134 | #define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3) | ||
135 | #define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4) | ||
136 | #define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5) | ||
137 | #define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6) | ||
138 | |||
139 | #define ES8328_LDACVOL 0x1a | ||
140 | #define ES8328_LDACVOL_MASK (0 << 0) | ||
141 | #define ES8328_LDACVOL_MAX (0xc0) | ||
142 | |||
143 | #define ES8328_RDACVOL 0x1b | ||
144 | #define ES8328_RDACVOL_MASK (0 << 0) | ||
145 | #define ES8328_RDACVOL_MAX (0xc0) | ||
146 | |||
147 | #define ES8328_DACVOL_MAX (0xc0) | ||
148 | |||
149 | #define ES8328_DACCONTROL4 0x1a | ||
150 | #define ES8328_DACCONTROL5 0x1b | ||
151 | |||
152 | #define ES8328_DACCONTROL6 0x1c | ||
153 | #define ES8328_DACCONTROL6_CLICKFREE (1 << 3) | ||
154 | #define ES8328_DACCONTROL6_DAC_INVR (1 << 4) | ||
155 | #define ES8328_DACCONTROL6_DAC_INVL (1 << 5) | ||
156 | #define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6) | ||
157 | #define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6) | ||
158 | #define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6) | ||
159 | #define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6) | ||
160 | |||
161 | #define ES8328_DACCONTROL7 0x1d | ||
162 | #define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0) | ||
163 | #define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0) | ||
164 | #define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0) | ||
165 | #define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0) | ||
166 | #define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */ | ||
167 | #define ES8328_DACCONTROL7_MONO (1 << 5) | ||
168 | #define ES8328_DACCONTROL7_ZEROR (1 << 6) | ||
169 | #define ES8328_DACCONTROL7_ZEROL (1 << 7) | ||
170 | |||
171 | /* Shelving filter */ | ||
172 | #define ES8328_DACCONTROL8 0x1e | ||
173 | #define ES8328_DACCONTROL9 0x1f | ||
174 | #define ES8328_DACCONTROL10 0x20 | ||
175 | #define ES8328_DACCONTROL11 0x21 | ||
176 | #define ES8328_DACCONTROL12 0x22 | ||
177 | #define ES8328_DACCONTROL13 0x23 | ||
178 | #define ES8328_DACCONTROL14 0x24 | ||
179 | #define ES8328_DACCONTROL15 0x25 | ||
180 | |||
181 | #define ES8328_DACCONTROL16 0x26 | ||
182 | #define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0) | ||
183 | #define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0) | ||
184 | #define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0) | ||
185 | #define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0) | ||
186 | #define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3) | ||
187 | #define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3) | ||
188 | #define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3) | ||
189 | #define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3) | ||
190 | |||
191 | #define ES8328_DACCONTROL17 0x27 | ||
192 | #define ES8328_DACCONTROL17_LI2LOVOL (7 << 3) | ||
193 | #define ES8328_DACCONTROL17_LI2LO (1 << 6) | ||
194 | #define ES8328_DACCONTROL17_LD2LO (1 << 7) | ||
195 | |||
196 | #define ES8328_DACCONTROL18 0x28 | ||
197 | #define ES8328_DACCONTROL18_RI2LOVOL (7 << 3) | ||
198 | #define ES8328_DACCONTROL18_RI2LO (1 << 6) | ||
199 | #define ES8328_DACCONTROL18_RD2LO (1 << 7) | ||
200 | |||
201 | #define ES8328_DACCONTROL19 0x29 | ||
202 | #define ES8328_DACCONTROL19_LI2ROVOL (7 << 3) | ||
203 | #define ES8328_DACCONTROL19_LI2RO (1 << 6) | ||
204 | #define ES8328_DACCONTROL19_LD2RO (1 << 7) | ||
205 | |||
206 | #define ES8328_DACCONTROL20 0x2a | ||
207 | #define ES8328_DACCONTROL20_RI2ROVOL (7 << 3) | ||
208 | #define ES8328_DACCONTROL20_RI2RO (1 << 6) | ||
209 | #define ES8328_DACCONTROL20_RD2RO (1 << 7) | ||
210 | |||
211 | #define ES8328_DACCONTROL21 0x2b | ||
212 | #define ES8328_DACCONTROL21_LI2MOVOL (7 << 3) | ||
213 | #define ES8328_DACCONTROL21_LI2MO (1 << 6) | ||
214 | #define ES8328_DACCONTROL21_LD2MO (1 << 7) | ||
215 | |||
216 | #define ES8328_DACCONTROL22 0x2c | ||
217 | #define ES8328_DACCONTROL22_RI2MOVOL (7 << 3) | ||
218 | #define ES8328_DACCONTROL22_RI2MO (1 << 6) | ||
219 | #define ES8328_DACCONTROL22_RD2MO (1 << 7) | ||
220 | |||
221 | #define ES8328_DACCONTROL23 0x2d | ||
222 | #define ES8328_DACCONTROL23_MOUTINV (1 << 1) | ||
223 | #define ES8328_DACCONTROL23_HPSWPOL (1 << 2) | ||
224 | #define ES8328_DACCONTROL23_HPSWEN (1 << 3) | ||
225 | #define ES8328_DACCONTROL23_VROI_1p5k (0 << 4) | ||
226 | #define ES8328_DACCONTROL23_VROI_40k (1 << 4) | ||
227 | #define ES8328_DACCONTROL23_OUT3_VREF (0 << 5) | ||
228 | #define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5) | ||
229 | #define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5) | ||
230 | #define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5) | ||
231 | #define ES8328_DACCONTROL23_ROUT2INV (1 << 7) | ||
232 | |||
233 | /* LOUT1 Amplifier */ | ||
234 | #define ES8328_LOUT1VOL 0x2e | ||
235 | #define ES8328_LOUT1VOL_MASK (0 << 5) | ||
236 | #define ES8328_LOUT1VOL_MAX (0x24) | ||
237 | |||
238 | /* ROUT1 Amplifier */ | ||
239 | #define ES8328_ROUT1VOL 0x2f | ||
240 | #define ES8328_ROUT1VOL_MASK (0 << 5) | ||
241 | #define ES8328_ROUT1VOL_MAX (0x24) | ||
242 | |||
243 | #define ES8328_OUT1VOL_MAX (0x24) | ||
244 | |||
245 | /* LOUT2 Amplifier */ | ||
246 | #define ES8328_LOUT2VOL 0x30 | ||
247 | #define ES8328_LOUT2VOL_MASK (0 << 5) | ||
248 | #define ES8328_LOUT2VOL_MAX (0x24) | ||
249 | |||
250 | /* ROUT2 Amplifier */ | ||
251 | #define ES8328_ROUT2VOL 0x31 | ||
252 | #define ES8328_ROUT2VOL_MASK (0 << 5) | ||
253 | #define ES8328_ROUT2VOL_MAX (0x24) | ||
254 | |||
255 | #define ES8328_OUT2VOL_MAX (0x24) | ||
256 | |||
257 | /* Mono Out Amplifier */ | ||
258 | #define ES8328_MONOOUTVOL 0x32 | ||
259 | #define ES8328_MONOOUTVOL_MASK (0 << 5) | ||
260 | #define ES8328_MONOOUTVOL_MAX (0x24) | ||
261 | |||
262 | #define ES8328_DACCONTROL29 0x33 | ||
263 | #define ES8328_DACCONTROL30 0x34 | ||
264 | |||
265 | #define ES8328_SYSCLK 0 | ||
266 | |||
267 | #define ES8328_REG_MAX 0x35 | ||
268 | |||
269 | #define ES8328_PLL1 0 | ||
270 | #define ES8328_PLL2 1 | ||
271 | |||
272 | /* clock inputs */ | ||
273 | #define ES8328_MCLK 0 | ||
274 | #define ES8328_PCMCLK 1 | ||
275 | |||
276 | /* clock divider id's */ | ||
277 | #define ES8328_PCMDIV 0 | ||
278 | #define ES8328_BCLKDIV 1 | ||
279 | #define ES8328_VXCLKDIV 2 | ||
280 | |||
281 | /* PCM clock dividers */ | ||
282 | #define ES8328_PCM_DIV_1 (0 << 6) | ||
283 | #define ES8328_PCM_DIV_3 (2 << 6) | ||
284 | #define ES8328_PCM_DIV_5_5 (3 << 6) | ||
285 | #define ES8328_PCM_DIV_2 (4 << 6) | ||
286 | #define ES8328_PCM_DIV_4 (5 << 6) | ||
287 | #define ES8328_PCM_DIV_6 (6 << 6) | ||
288 | #define ES8328_PCM_DIV_8 (7 << 6) | ||
289 | |||
290 | /* BCLK clock dividers */ | ||
291 | #define ES8328_BCLK_DIV_1 (0 << 7) | ||
292 | #define ES8328_BCLK_DIV_2 (1 << 7) | ||
293 | #define ES8328_BCLK_DIV_4 (2 << 7) | ||
294 | #define ES8328_BCLK_DIV_8 (3 << 7) | ||
295 | |||
296 | /* VXCLK clock dividers */ | ||
297 | #define ES8328_VXCLK_DIV_1 (0 << 6) | ||
298 | #define ES8328_VXCLK_DIV_2 (1 << 6) | ||
299 | #define ES8328_VXCLK_DIV_4 (2 << 6) | ||
300 | #define ES8328_VXCLK_DIV_8 (3 << 6) | ||
301 | #define ES8328_VXCLK_DIV_16 (4 << 6) | ||
302 | |||
303 | #define ES8328_DAI_HIFI 0 | ||
304 | #define ES8328_DAI_VOICE 1 | ||
305 | |||
306 | #define ES8328_1536FS 1536 | ||
307 | #define ES8328_1024FS 1024 | ||
308 | #define ES8328_768FS 768 | ||
309 | #define ES8328_512FS 512 | ||
310 | #define ES8328_384FS 384 | ||
311 | #define ES8328_256FS 256 | ||
312 | #define ES8328_128FS 128 | ||
313 | |||
314 | #endif | ||
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index f3012b645b51..6164e78b466a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -240,6 +240,18 @@ config SND_SOC_IMX_WM8962 | |||
240 | Say Y if you want to add support for SoC audio on an i.MX board with | 240 | Say Y if you want to add support for SoC audio on an i.MX board with |
241 | a wm8962 codec. | 241 | a wm8962 codec. |
242 | 242 | ||
243 | config SND_SOC_IMX_ES8328 | ||
244 | tristate "SoC Audio support for i.MX boards with the ES8328 codec" | ||
245 | depends on OF && (I2C || SPI) | ||
246 | select SND_SOC_ES8328_I2C if I2C | ||
247 | select SND_SOC_ES8328_SPI if SPI_MASTER | ||
248 | select SND_SOC_IMX_PCM_DMA | ||
249 | select SND_SOC_IMX_AUDMUX | ||
250 | select SND_SOC_FSL_SSI | ||
251 | help | ||
252 | Say Y if you want to add support for the ES8328 audio codec connected | ||
253 | via SSI/I2S over either SPI or I2C. | ||
254 | |||
243 | config SND_SOC_IMX_SGTL5000 | 255 | config SND_SOC_IMX_SGTL5000 |
244 | tristate "SoC Audio support for i.MX boards with sgtl5000" | 256 | tristate "SoC Audio support for i.MX boards with sgtl5000" |
245 | depends on OF && I2C | 257 | depends on OF && I2C |
@@ -268,6 +280,23 @@ config SND_SOC_IMX_MC13783 | |||
268 | select SND_SOC_MC13783 | 280 | select SND_SOC_MC13783 |
269 | select SND_SOC_IMX_PCM_DMA | 281 | select SND_SOC_IMX_PCM_DMA |
270 | 282 | ||
283 | config SND_SOC_FSL_ASOC_CARD | ||
284 | tristate "Generic ASoC Sound Card with ASRC support" | ||
285 | depends on OF && I2C | ||
286 | select SND_SOC_IMX_AUDMUX | ||
287 | select SND_SOC_IMX_PCM_DMA | ||
288 | select SND_SOC_FSL_ESAI | ||
289 | select SND_SOC_FSL_SAI | ||
290 | select SND_SOC_FSL_SSI | ||
291 | select SND_SOC_CS42XX8_I2C | ||
292 | select SND_SOC_SGTL5000 | ||
293 | select SND_SOC_WM8962 | ||
294 | help | ||
295 | ALSA SoC Audio support with ASRC feature for Freescale SoCs that have | ||
296 | ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888 | ||
297 | and SGTL5000. | ||
298 | Say Y if you want to add support for Freescale Generic ASoC Sound Card. | ||
299 | |||
271 | endif # SND_IMX_SOC | 300 | endif # SND_IMX_SOC |
272 | 301 | ||
273 | endmenu | 302 | endmenu |
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 9ff59267eac9..d28dc25c9375 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o | |||
11 | obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o | 11 | obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o |
12 | 12 | ||
13 | # Freescale SSI/DMA/SAI/SPDIF Support | 13 | # Freescale SSI/DMA/SAI/SPDIF Support |
14 | snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o | ||
14 | snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o | 15 | snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o |
15 | snd-soc-fsl-sai-objs := fsl_sai.o | 16 | snd-soc-fsl-sai-objs := fsl_sai.o |
16 | snd-soc-fsl-ssi-y := fsl_ssi.o | 17 | snd-soc-fsl-ssi-y := fsl_ssi.o |
@@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o | |||
19 | snd-soc-fsl-esai-objs := fsl_esai.o | 20 | snd-soc-fsl-esai-objs := fsl_esai.o |
20 | snd-soc-fsl-utils-objs := fsl_utils.o | 21 | snd-soc-fsl-utils-objs := fsl_utils.o |
21 | snd-soc-fsl-dma-objs := fsl_dma.o | 22 | snd-soc-fsl-dma-objs := fsl_dma.o |
23 | obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o | ||
22 | obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o | 24 | obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o |
23 | obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o | 25 | obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o |
24 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o | 26 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o |
@@ -50,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o | |||
50 | snd-soc-phycore-ac97-objs := phycore-ac97.o | 52 | snd-soc-phycore-ac97-objs := phycore-ac97.o |
51 | snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o | 53 | snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o |
52 | snd-soc-wm1133-ev1-objs := wm1133-ev1.o | 54 | snd-soc-wm1133-ev1-objs := wm1133-ev1.o |
55 | snd-soc-imx-es8328-objs := imx-es8328.o | ||
53 | snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o | 56 | snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o |
54 | snd-soc-imx-wm8962-objs := imx-wm8962.o | 57 | snd-soc-imx-wm8962-objs := imx-wm8962.o |
55 | snd-soc-imx-spdif-objs := imx-spdif.o | 58 | snd-soc-imx-spdif-objs := imx-spdif.o |
@@ -59,6 +62,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o | |||
59 | obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o | 62 | obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o |
60 | obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o | 63 | obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o |
61 | obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o | 64 | obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o |
65 | obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o | ||
62 | obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o | 66 | obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o |
63 | obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o | 67 | obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o |
64 | obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o | 68 | obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o |
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c new file mode 100644 index 000000000000..007c772f3cef --- /dev/null +++ b/sound/soc/fsl/fsl-asoc-card.c | |||
@@ -0,0 +1,574 @@ | |||
1 | /* | ||
2 | * Freescale Generic ASoC Sound Card driver with ASRC | ||
3 | * | ||
4 | * Copyright (C) 2014 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Author: Nicolin Chen <nicoleotsuka@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | #include <sound/soc.h> | ||
19 | |||
20 | #include "fsl_esai.h" | ||
21 | #include "fsl_sai.h" | ||
22 | #include "imx-audmux.h" | ||
23 | |||
24 | #include "../codecs/sgtl5000.h" | ||
25 | #include "../codecs/wm8962.h" | ||
26 | |||
27 | #define RX 0 | ||
28 | #define TX 1 | ||
29 | |||
30 | /* Default DAI format without Master and Slave flag */ | ||
31 | #define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF) | ||
32 | |||
33 | /** | ||
34 | * CODEC private data | ||
35 | * | ||
36 | * @mclk_freq: Clock rate of MCLK | ||
37 | * @mclk_id: MCLK (or main clock) id for set_sysclk() | ||
38 | * @fll_id: FLL (or secordary clock) id for set_sysclk() | ||
39 | * @pll_id: PLL id for set_pll() | ||
40 | */ | ||
41 | struct codec_priv { | ||
42 | unsigned long mclk_freq; | ||
43 | u32 mclk_id; | ||
44 | u32 fll_id; | ||
45 | u32 pll_id; | ||
46 | }; | ||
47 | |||
48 | /** | ||
49 | * CPU private data | ||
50 | * | ||
51 | * @sysclk_freq[2]: SYSCLK rates for set_sysclk() | ||
52 | * @sysclk_dir[2]: SYSCLK directions for set_sysclk() | ||
53 | * @sysclk_id[2]: SYSCLK ids for set_sysclk() | ||
54 | * | ||
55 | * Note: [1] for tx and [0] for rx | ||
56 | */ | ||
57 | struct cpu_priv { | ||
58 | unsigned long sysclk_freq[2]; | ||
59 | u32 sysclk_dir[2]; | ||
60 | u32 sysclk_id[2]; | ||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * Freescale Generic ASOC card private data | ||
65 | * | ||
66 | * @dai_link[3]: DAI link structure including normal one and DPCM link | ||
67 | * @pdev: platform device pointer | ||
68 | * @codec_priv: CODEC private data | ||
69 | * @cpu_priv: CPU private data | ||
70 | * @card: ASoC card structure | ||
71 | * @sample_rate: Current sample rate | ||
72 | * @sample_format: Current sample format | ||
73 | * @asrc_rate: ASRC sample rate used by Back-Ends | ||
74 | * @asrc_format: ASRC sample format used by Back-Ends | ||
75 | * @dai_fmt: DAI format between CPU and CODEC | ||
76 | * @name: Card name | ||
77 | */ | ||
78 | |||
79 | struct fsl_asoc_card_priv { | ||
80 | struct snd_soc_dai_link dai_link[3]; | ||
81 | struct platform_device *pdev; | ||
82 | struct codec_priv codec_priv; | ||
83 | struct cpu_priv cpu_priv; | ||
84 | struct snd_soc_card card; | ||
85 | u32 sample_rate; | ||
86 | u32 sample_format; | ||
87 | u32 asrc_rate; | ||
88 | u32 asrc_format; | ||
89 | u32 dai_fmt; | ||
90 | char name[32]; | ||
91 | }; | ||
92 | |||
93 | /** | ||
94 | * This dapm route map exsits for DPCM link only. | ||
95 | * The other routes shall go through Device Tree. | ||
96 | */ | ||
97 | static const struct snd_soc_dapm_route audio_map[] = { | ||
98 | {"CPU-Playback", NULL, "ASRC-Playback"}, | ||
99 | {"Playback", NULL, "CPU-Playback"}, | ||
100 | {"ASRC-Capture", NULL, "CPU-Capture"}, | ||
101 | {"CPU-Capture", NULL, "Capture"}, | ||
102 | }; | ||
103 | |||
104 | /* Add all possible widgets into here without being redundant */ | ||
105 | static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { | ||
106 | SND_SOC_DAPM_LINE("Line Out Jack", NULL), | ||
107 | SND_SOC_DAPM_LINE("Line In Jack", NULL), | ||
108 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
109 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
110 | SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
111 | SND_SOC_DAPM_MIC("AMIC", NULL), | ||
112 | SND_SOC_DAPM_MIC("DMIC", NULL), | ||
113 | }; | ||
114 | |||
115 | static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, | ||
116 | struct snd_pcm_hw_params *params) | ||
117 | { | ||
118 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
119 | struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
120 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
121 | struct cpu_priv *cpu_priv = &priv->cpu_priv; | ||
122 | struct device *dev = rtd->card->dev; | ||
123 | int ret; | ||
124 | |||
125 | priv->sample_rate = params_rate(params); | ||
126 | priv->sample_format = params_format(params); | ||
127 | |||
128 | if (priv->card.set_bias_level) | ||
129 | return 0; | ||
130 | |||
131 | /* Specific configurations of DAIs starts from here */ | ||
132 | ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], | ||
133 | cpu_priv->sysclk_freq[tx], | ||
134 | cpu_priv->sysclk_dir[tx]); | ||
135 | if (ret) { | ||
136 | dev_err(dev, "failed to set sysclk for cpu dai\n"); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static struct snd_soc_ops fsl_asoc_card_ops = { | ||
144 | .hw_params = fsl_asoc_card_hw_params, | ||
145 | }; | ||
146 | |||
147 | static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | ||
148 | struct snd_pcm_hw_params *params) | ||
149 | { | ||
150 | struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
151 | struct snd_interval *rate; | ||
152 | struct snd_mask *mask; | ||
153 | |||
154 | rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
155 | rate->max = rate->min = priv->asrc_rate; | ||
156 | |||
157 | mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
158 | snd_mask_none(mask); | ||
159 | snd_mask_set(mask, priv->asrc_format); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static struct snd_soc_dai_link fsl_asoc_card_dai[] = { | ||
165 | /* Default ASoC DAI Link*/ | ||
166 | { | ||
167 | .name = "HiFi", | ||
168 | .stream_name = "HiFi", | ||
169 | .ops = &fsl_asoc_card_ops, | ||
170 | }, | ||
171 | /* DPCM Link between Front-End and Back-End (Optional) */ | ||
172 | { | ||
173 | .name = "HiFi-ASRC-FE", | ||
174 | .stream_name = "HiFi-ASRC-FE", | ||
175 | .codec_name = "snd-soc-dummy", | ||
176 | .codec_dai_name = "snd-soc-dummy-dai", | ||
177 | .dpcm_playback = 1, | ||
178 | .dpcm_capture = 1, | ||
179 | .dynamic = 1, | ||
180 | }, | ||
181 | { | ||
182 | .name = "HiFi-ASRC-BE", | ||
183 | .stream_name = "HiFi-ASRC-BE", | ||
184 | .platform_name = "snd-soc-dummy", | ||
185 | .be_hw_params_fixup = be_hw_params_fixup, | ||
186 | .ops = &fsl_asoc_card_ops, | ||
187 | .dpcm_playback = 1, | ||
188 | .dpcm_capture = 1, | ||
189 | .no_pcm = 1, | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, | ||
194 | struct snd_soc_dapm_context *dapm, | ||
195 | enum snd_soc_bias_level level) | ||
196 | { | ||
197 | struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); | ||
198 | struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; | ||
199 | struct codec_priv *codec_priv = &priv->codec_priv; | ||
200 | struct device *dev = card->dev; | ||
201 | unsigned int pll_out; | ||
202 | int ret; | ||
203 | |||
204 | if (dapm->dev != codec_dai->dev) | ||
205 | return 0; | ||
206 | |||
207 | switch (level) { | ||
208 | case SND_SOC_BIAS_PREPARE: | ||
209 | if (dapm->bias_level != SND_SOC_BIAS_STANDBY) | ||
210 | break; | ||
211 | |||
212 | if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) | ||
213 | pll_out = priv->sample_rate * 384; | ||
214 | else | ||
215 | pll_out = priv->sample_rate * 256; | ||
216 | |||
217 | ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, | ||
218 | codec_priv->mclk_id, | ||
219 | codec_priv->mclk_freq, pll_out); | ||
220 | if (ret) { | ||
221 | dev_err(dev, "failed to start FLL: %d\n", ret); | ||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id, | ||
226 | pll_out, SND_SOC_CLOCK_IN); | ||
227 | if (ret) { | ||
228 | dev_err(dev, "failed to set SYSCLK: %d\n", ret); | ||
229 | return ret; | ||
230 | } | ||
231 | break; | ||
232 | |||
233 | case SND_SOC_BIAS_STANDBY: | ||
234 | if (dapm->bias_level != SND_SOC_BIAS_PREPARE) | ||
235 | break; | ||
236 | |||
237 | ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, | ||
238 | codec_priv->mclk_freq, | ||
239 | SND_SOC_CLOCK_IN); | ||
240 | if (ret) { | ||
241 | dev_err(dev, "failed to switch away from FLL: %d\n", ret); | ||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0); | ||
246 | if (ret) { | ||
247 | dev_err(dev, "failed to stop FLL: %d\n", ret); | ||
248 | return ret; | ||
249 | } | ||
250 | break; | ||
251 | |||
252 | default: | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int fsl_asoc_card_audmux_init(struct device_node *np, | ||
260 | struct fsl_asoc_card_priv *priv) | ||
261 | { | ||
262 | struct device *dev = &priv->pdev->dev; | ||
263 | u32 int_ptcr = 0, ext_ptcr = 0; | ||
264 | int int_port, ext_port; | ||
265 | int ret; | ||
266 | |||
267 | ret = of_property_read_u32(np, "mux-int-port", &int_port); | ||
268 | if (ret) { | ||
269 | dev_err(dev, "mux-int-port missing or invalid\n"); | ||
270 | return ret; | ||
271 | } | ||
272 | ret = of_property_read_u32(np, "mux-ext-port", &ext_port); | ||
273 | if (ret) { | ||
274 | dev_err(dev, "mux-ext-port missing or invalid\n"); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * The port numbering in the hardware manual starts at 1, while | ||
280 | * the AUDMUX API expects it starts at 0. | ||
281 | */ | ||
282 | int_port--; | ||
283 | ext_port--; | ||
284 | |||
285 | /* | ||
286 | * Use asynchronous mode (6 wires) for all cases. | ||
287 | * If only 4 wires are needed, just set SSI into | ||
288 | * synchronous mode and enable 4 PADs in IOMUX. | ||
289 | */ | ||
290 | switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
291 | case SND_SOC_DAIFMT_CBM_CFM: | ||
292 | int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | | ||
293 | IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | | ||
294 | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | | ||
295 | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | | ||
296 | IMX_AUDMUX_V2_PTCR_RFSDIR | | ||
297 | IMX_AUDMUX_V2_PTCR_RCLKDIR | | ||
298 | IMX_AUDMUX_V2_PTCR_TFSDIR | | ||
299 | IMX_AUDMUX_V2_PTCR_TCLKDIR; | ||
300 | break; | ||
301 | case SND_SOC_DAIFMT_CBM_CFS: | ||
302 | int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | | ||
303 | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | | ||
304 | IMX_AUDMUX_V2_PTCR_RCLKDIR | | ||
305 | IMX_AUDMUX_V2_PTCR_TCLKDIR; | ||
306 | ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | | ||
307 | IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | | ||
308 | IMX_AUDMUX_V2_PTCR_RFSDIR | | ||
309 | IMX_AUDMUX_V2_PTCR_TFSDIR; | ||
310 | break; | ||
311 | case SND_SOC_DAIFMT_CBS_CFM: | ||
312 | int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | | ||
313 | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | | ||
314 | IMX_AUDMUX_V2_PTCR_RFSDIR | | ||
315 | IMX_AUDMUX_V2_PTCR_TFSDIR; | ||
316 | ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | | ||
317 | IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | | ||
318 | IMX_AUDMUX_V2_PTCR_RCLKDIR | | ||
319 | IMX_AUDMUX_V2_PTCR_TCLKDIR; | ||
320 | break; | ||
321 | case SND_SOC_DAIFMT_CBS_CFS: | ||
322 | ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | | ||
323 | IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | | ||
324 | IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | | ||
325 | IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | | ||
326 | IMX_AUDMUX_V2_PTCR_RFSDIR | | ||
327 | IMX_AUDMUX_V2_PTCR_RCLKDIR | | ||
328 | IMX_AUDMUX_V2_PTCR_TFSDIR | | ||
329 | IMX_AUDMUX_V2_PTCR_TCLKDIR; | ||
330 | break; | ||
331 | default: | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | /* Asynchronous mode can not be set along with RCLKDIR */ | ||
336 | ret = imx_audmux_v2_configure_port(int_port, 0, | ||
337 | IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); | ||
338 | if (ret) { | ||
339 | dev_err(dev, "audmux internal port setup failed\n"); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | ret = imx_audmux_v2_configure_port(int_port, int_ptcr, | ||
344 | IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); | ||
345 | if (ret) { | ||
346 | dev_err(dev, "audmux internal port setup failed\n"); | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | ret = imx_audmux_v2_configure_port(ext_port, 0, | ||
351 | IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); | ||
352 | if (ret) { | ||
353 | dev_err(dev, "audmux external port setup failed\n"); | ||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, | ||
358 | IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); | ||
359 | if (ret) { | ||
360 | dev_err(dev, "audmux external port setup failed\n"); | ||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static int fsl_asoc_card_late_probe(struct snd_soc_card *card) | ||
368 | { | ||
369 | struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); | ||
370 | struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; | ||
371 | struct codec_priv *codec_priv = &priv->codec_priv; | ||
372 | struct device *dev = card->dev; | ||
373 | int ret; | ||
374 | |||
375 | ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, | ||
376 | codec_priv->mclk_freq, SND_SOC_CLOCK_IN); | ||
377 | if (ret) { | ||
378 | dev_err(dev, "failed to set sysclk in %s\n", __func__); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static int fsl_asoc_card_probe(struct platform_device *pdev) | ||
386 | { | ||
387 | struct device_node *cpu_np, *codec_np, *asrc_np; | ||
388 | struct device_node *np = pdev->dev.of_node; | ||
389 | struct platform_device *asrc_pdev = NULL; | ||
390 | struct platform_device *cpu_pdev; | ||
391 | struct fsl_asoc_card_priv *priv; | ||
392 | struct i2c_client *codec_dev; | ||
393 | struct clk *codec_clk; | ||
394 | u32 width; | ||
395 | int ret; | ||
396 | |||
397 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
398 | if (!priv) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | cpu_np = of_parse_phandle(np, "audio-cpu", 0); | ||
402 | /* Give a chance to old DT binding */ | ||
403 | if (!cpu_np) | ||
404 | cpu_np = of_parse_phandle(np, "ssi-controller", 0); | ||
405 | codec_np = of_parse_phandle(np, "audio-codec", 0); | ||
406 | if (!cpu_np || !codec_np) { | ||
407 | dev_err(&pdev->dev, "phandle missing or invalid\n"); | ||
408 | ret = -EINVAL; | ||
409 | goto fail; | ||
410 | } | ||
411 | |||
412 | cpu_pdev = of_find_device_by_node(cpu_np); | ||
413 | if (!cpu_pdev) { | ||
414 | dev_err(&pdev->dev, "failed to find CPU DAI device\n"); | ||
415 | ret = -EINVAL; | ||
416 | goto fail; | ||
417 | } | ||
418 | |||
419 | codec_dev = of_find_i2c_device_by_node(codec_np); | ||
420 | if (!codec_dev) { | ||
421 | dev_err(&pdev->dev, "failed to find codec platform device\n"); | ||
422 | ret = -EINVAL; | ||
423 | goto fail; | ||
424 | } | ||
425 | |||
426 | asrc_np = of_parse_phandle(np, "audio-asrc", 0); | ||
427 | if (asrc_np) | ||
428 | asrc_pdev = of_find_device_by_node(asrc_np); | ||
429 | |||
430 | /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ | ||
431 | codec_clk = clk_get(&codec_dev->dev, NULL); | ||
432 | if (!IS_ERR(codec_clk)) { | ||
433 | priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); | ||
434 | clk_put(codec_clk); | ||
435 | } | ||
436 | |||
437 | /* Default sample rate and format, will be updated in hw_params() */ | ||
438 | priv->sample_rate = 44100; | ||
439 | priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; | ||
440 | |||
441 | /* Assign a default DAI format, and allow each card to overwrite it */ | ||
442 | priv->dai_fmt = DAI_FMT_BASE; | ||
443 | |||
444 | /* Diversify the card configurations */ | ||
445 | if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { | ||
446 | priv->card.set_bias_level = NULL; | ||
447 | priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; | ||
448 | priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; | ||
449 | priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; | ||
450 | priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; | ||
451 | priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; | ||
452 | } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { | ||
453 | priv->codec_priv.mclk_id = SGTL5000_SYSCLK; | ||
454 | priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; | ||
455 | } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { | ||
456 | priv->card.set_bias_level = fsl_asoc_card_set_bias_level; | ||
457 | priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; | ||
458 | priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; | ||
459 | priv->codec_priv.pll_id = WM8962_FLL; | ||
460 | priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; | ||
461 | } else { | ||
462 | dev_err(&pdev->dev, "unknown Device Tree compatible\n"); | ||
463 | return -EINVAL; | ||
464 | } | ||
465 | |||
466 | /* Common settings for corresponding Freescale CPU DAI driver */ | ||
467 | if (strstr(cpu_np->name, "ssi")) { | ||
468 | /* Only SSI needs to configure AUDMUX */ | ||
469 | ret = fsl_asoc_card_audmux_init(np, priv); | ||
470 | if (ret) { | ||
471 | dev_err(&pdev->dev, "failed to init audmux\n"); | ||
472 | goto asrc_fail; | ||
473 | } | ||
474 | } else if (strstr(cpu_np->name, "esai")) { | ||
475 | priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; | ||
476 | priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; | ||
477 | } else if (strstr(cpu_np->name, "sai")) { | ||
478 | priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; | ||
479 | priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; | ||
480 | } | ||
481 | |||
482 | sprintf(priv->name, "%s-audio", codec_dev->name); | ||
483 | |||
484 | /* Initialize sound card */ | ||
485 | priv->pdev = pdev; | ||
486 | priv->card.dev = &pdev->dev; | ||
487 | priv->card.name = priv->name; | ||
488 | priv->card.dai_link = priv->dai_link; | ||
489 | priv->card.dapm_routes = audio_map; | ||
490 | priv->card.late_probe = fsl_asoc_card_late_probe; | ||
491 | priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); | ||
492 | priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; | ||
493 | priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); | ||
494 | |||
495 | memcpy(priv->dai_link, fsl_asoc_card_dai, | ||
496 | sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); | ||
497 | |||
498 | /* Normal DAI Link */ | ||
499 | priv->dai_link[0].cpu_of_node = cpu_np; | ||
500 | priv->dai_link[0].codec_of_node = codec_np; | ||
501 | priv->dai_link[0].codec_dai_name = codec_dev->name; | ||
502 | priv->dai_link[0].platform_of_node = cpu_np; | ||
503 | priv->dai_link[0].dai_fmt = priv->dai_fmt; | ||
504 | priv->card.num_links = 1; | ||
505 | |||
506 | if (asrc_pdev) { | ||
507 | /* DPCM DAI Links only if ASRC exsits */ | ||
508 | priv->dai_link[1].cpu_of_node = asrc_np; | ||
509 | priv->dai_link[1].platform_of_node = asrc_np; | ||
510 | priv->dai_link[2].codec_dai_name = codec_dev->name; | ||
511 | priv->dai_link[2].codec_of_node = codec_np; | ||
512 | priv->dai_link[2].cpu_of_node = cpu_np; | ||
513 | priv->dai_link[2].dai_fmt = priv->dai_fmt; | ||
514 | priv->card.num_links = 3; | ||
515 | |||
516 | ret = of_property_read_u32(asrc_np, "fsl,asrc-rate", | ||
517 | &priv->asrc_rate); | ||
518 | if (ret) { | ||
519 | dev_err(&pdev->dev, "failed to get output rate\n"); | ||
520 | ret = -EINVAL; | ||
521 | goto asrc_fail; | ||
522 | } | ||
523 | |||
524 | ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width); | ||
525 | if (ret) { | ||
526 | dev_err(&pdev->dev, "failed to get output rate\n"); | ||
527 | ret = -EINVAL; | ||
528 | goto asrc_fail; | ||
529 | } | ||
530 | |||
531 | if (width == 24) | ||
532 | priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE; | ||
533 | else | ||
534 | priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE; | ||
535 | } | ||
536 | |||
537 | /* Finish card registering */ | ||
538 | platform_set_drvdata(pdev, priv); | ||
539 | snd_soc_card_set_drvdata(&priv->card, priv); | ||
540 | |||
541 | ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); | ||
542 | if (ret) | ||
543 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); | ||
544 | |||
545 | asrc_fail: | ||
546 | of_node_put(asrc_np); | ||
547 | fail: | ||
548 | of_node_put(codec_np); | ||
549 | of_node_put(cpu_np); | ||
550 | |||
551 | return ret; | ||
552 | } | ||
553 | |||
554 | static const struct of_device_id fsl_asoc_card_dt_ids[] = { | ||
555 | { .compatible = "fsl,imx-audio-cs42888", }, | ||
556 | { .compatible = "fsl,imx-audio-sgtl5000", }, | ||
557 | { .compatible = "fsl,imx-audio-wm8962", }, | ||
558 | {} | ||
559 | }; | ||
560 | |||
561 | static struct platform_driver fsl_asoc_card_driver = { | ||
562 | .probe = fsl_asoc_card_probe, | ||
563 | .driver = { | ||
564 | .name = "fsl-asoc-card", | ||
565 | .pm = &snd_soc_pm_ops, | ||
566 | .of_match_table = fsl_asoc_card_dt_ids, | ||
567 | }, | ||
568 | }; | ||
569 | module_platform_driver(fsl_asoc_card_driver); | ||
570 | |||
571 | MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC"); | ||
572 | MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>"); | ||
573 | MODULE_ALIAS("platform:fsl-asoc-card"); | ||
574 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 822110420b71..3b145313f93e 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c | |||
@@ -684,7 +684,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) | |||
684 | } | 684 | } |
685 | } | 685 | } |
686 | 686 | ||
687 | static struct regmap_config fsl_asrc_regmap_config = { | 687 | static const struct regmap_config fsl_asrc_regmap_config = { |
688 | .reg_bits = 32, | 688 | .reg_bits = 32, |
689 | .reg_stride = 4, | 689 | .reg_stride = 4, |
690 | .val_bits = 32, | 690 | .val_bits = 32, |
@@ -802,10 +802,6 @@ static int fsl_asrc_probe(struct platform_device *pdev) | |||
802 | 802 | ||
803 | asrc_priv->paddr = res->start; | 803 | asrc_priv->paddr = res->start; |
804 | 804 | ||
805 | /* Register regmap and let it prepare core clock */ | ||
806 | if (of_property_read_bool(np, "big-endian")) | ||
807 | fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
808 | |||
809 | asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs, | 805 | asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs, |
810 | &fsl_asrc_regmap_config); | 806 | &fsl_asrc_regmap_config); |
811 | if (IS_ERR(asrc_priv->regmap)) { | 807 | if (IS_ERR(asrc_priv->regmap)) { |
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index a3b29ed84963..8bcdfda09d7a 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c | |||
@@ -37,6 +37,7 @@ | |||
37 | * @fsysclk: system clock source to derive HCK, SCK and FS | 37 | * @fsysclk: system clock source to derive HCK, SCK and FS |
38 | * @fifo_depth: depth of tx/rx FIFO | 38 | * @fifo_depth: depth of tx/rx FIFO |
39 | * @slot_width: width of each DAI slot | 39 | * @slot_width: width of each DAI slot |
40 | * @slots: number of slots | ||
40 | * @hck_rate: clock rate of desired HCKx clock | 41 | * @hck_rate: clock rate of desired HCKx clock |
41 | * @sck_rate: clock rate of desired SCKx clock | 42 | * @sck_rate: clock rate of desired SCKx clock |
42 | * @hck_dir: the direction of HCKx pads | 43 | * @hck_dir: the direction of HCKx pads |
@@ -55,6 +56,7 @@ struct fsl_esai { | |||
55 | struct clk *fsysclk; | 56 | struct clk *fsysclk; |
56 | u32 fifo_depth; | 57 | u32 fifo_depth; |
57 | u32 slot_width; | 58 | u32 slot_width; |
59 | u32 slots; | ||
58 | u32 hck_rate[2]; | 60 | u32 hck_rate[2]; |
59 | u32 sck_rate[2]; | 61 | u32 sck_rate[2]; |
60 | bool hck_dir[2]; | 62 | bool hck_dir[2]; |
@@ -362,6 +364,7 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, | |||
362 | ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask)); | 364 | ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask)); |
363 | 365 | ||
364 | esai_priv->slot_width = slot_width; | 366 | esai_priv->slot_width = slot_width; |
367 | esai_priv->slots = slots; | ||
365 | 368 | ||
366 | return 0; | 369 | return 0; |
367 | } | 370 | } |
@@ -509,10 +512,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, | |||
509 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | 512 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
510 | u32 width = snd_pcm_format_width(params_format(params)); | 513 | u32 width = snd_pcm_format_width(params_format(params)); |
511 | u32 channels = params_channels(params); | 514 | u32 channels = params_channels(params); |
515 | u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); | ||
512 | u32 bclk, mask, val; | 516 | u32 bclk, mask, val; |
513 | int ret; | 517 | int ret; |
514 | 518 | ||
515 | bclk = params_rate(params) * esai_priv->slot_width * 2; | 519 | bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots; |
516 | 520 | ||
517 | ret = fsl_esai_set_bclk(dai, tx, bclk); | 521 | ret = fsl_esai_set_bclk(dai, tx, bclk); |
518 | if (ret) | 522 | if (ret) |
@@ -529,7 +533,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, | |||
529 | mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | | 533 | mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | |
530 | (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); | 534 | (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); |
531 | val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | | 535 | val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | |
532 | (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels)); | 536 | (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins)); |
533 | 537 | ||
534 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); | 538 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); |
535 | 539 | ||
@@ -564,6 +568,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
564 | struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); | 568 | struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); |
565 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | 569 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
566 | u8 i, channels = substream->runtime->channels; | 570 | u8 i, channels = substream->runtime->channels; |
571 | u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); | ||
567 | 572 | ||
568 | switch (cmd) { | 573 | switch (cmd) { |
569 | case SNDRV_PCM_TRIGGER_START: | 574 | case SNDRV_PCM_TRIGGER_START: |
@@ -578,7 +583,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
578 | 583 | ||
579 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), | 584 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
580 | tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, | 585 | tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, |
581 | tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels)); | 586 | tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); |
582 | break; | 587 | break; |
583 | case SNDRV_PCM_TRIGGER_SUSPEND: | 588 | case SNDRV_PCM_TRIGGER_SUSPEND: |
584 | case SNDRV_PCM_TRIGGER_STOP: | 589 | case SNDRV_PCM_TRIGGER_STOP: |
@@ -705,7 +710,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) | |||
705 | } | 710 | } |
706 | } | 711 | } |
707 | 712 | ||
708 | static struct regmap_config fsl_esai_regmap_config = { | 713 | static const struct regmap_config fsl_esai_regmap_config = { |
709 | .reg_bits = 32, | 714 | .reg_bits = 32, |
710 | .reg_stride = 4, | 715 | .reg_stride = 4, |
711 | .val_bits = 32, | 716 | .val_bits = 32, |
@@ -731,9 +736,6 @@ static int fsl_esai_probe(struct platform_device *pdev) | |||
731 | esai_priv->pdev = pdev; | 736 | esai_priv->pdev = pdev; |
732 | strcpy(esai_priv->name, np->name); | 737 | strcpy(esai_priv->name, np->name); |
733 | 738 | ||
734 | if (of_property_read_bool(np, "big-endian")) | ||
735 | fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
736 | |||
737 | /* Get the addresses and IRQ */ | 739 | /* Get the addresses and IRQ */ |
738 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 740 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
739 | regs = devm_ioremap_resource(&pdev->dev, res); | 741 | regs = devm_ioremap_resource(&pdev->dev, res); |
@@ -781,6 +783,9 @@ static int fsl_esai_probe(struct platform_device *pdev) | |||
781 | /* Set a default slot size */ | 783 | /* Set a default slot size */ |
782 | esai_priv->slot_width = 32; | 784 | esai_priv->slot_width = 32; |
783 | 785 | ||
786 | /* Set a default slot number */ | ||
787 | esai_priv->slots = 2; | ||
788 | |||
784 | /* Set a default master/slave state */ | 789 | /* Set a default master/slave state */ |
785 | esai_priv->slave_mode = true; | 790 | esai_priv->slave_mode = true; |
786 | 791 | ||
diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index 75e14033e8d8..91a550f4a10d 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h | |||
@@ -130,8 +130,8 @@ | |||
130 | #define ESAI_xFCR_RE_WIDTH 4 | 130 | #define ESAI_xFCR_RE_WIDTH 4 |
131 | #define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) | 131 | #define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) |
132 | #define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) | 132 | #define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) |
133 | #define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK) | 133 | #define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - x)) & ESAI_xFCR_TE_MASK) |
134 | #define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK) | 134 | #define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - x)) & ESAI_xFCR_RE_MASK) |
135 | #define ESAI_xFCR_xFR_SHIFT 1 | 135 | #define ESAI_xFCR_xFR_SHIFT 1 |
136 | #define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT) | 136 | #define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT) |
137 | #define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT) | 137 | #define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT) |
@@ -272,8 +272,8 @@ | |||
272 | #define ESAI_xCR_RE_WIDTH 4 | 272 | #define ESAI_xCR_RE_WIDTH 4 |
273 | #define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) | 273 | #define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) |
274 | #define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) | 274 | #define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) |
275 | #define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK) | 275 | #define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - x)) & ESAI_xCR_TE_MASK) |
276 | #define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK) | 276 | #define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - x)) & ESAI_xCR_RE_MASK) |
277 | 277 | ||
278 | /* | 278 | /* |
279 | * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8 | 279 | * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8 |
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index faa049797897..52d1e9982639 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c | |||
@@ -539,7 +539,7 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) | |||
539 | } | 539 | } |
540 | } | 540 | } |
541 | 541 | ||
542 | static struct regmap_config fsl_sai_regmap_config = { | 542 | static const struct regmap_config fsl_sai_regmap_config = { |
543 | .reg_bits = 32, | 543 | .reg_bits = 32, |
544 | .reg_stride = 4, | 544 | .reg_stride = 4, |
545 | .val_bits = 32, | 545 | .val_bits = 32, |
@@ -568,10 +568,6 @@ static int fsl_sai_probe(struct platform_device *pdev) | |||
568 | if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) | 568 | if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) |
569 | sai->sai_on_imx = true; | 569 | sai->sai_on_imx = true; |
570 | 570 | ||
571 | sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); | ||
572 | if (sai->big_endian_regs) | ||
573 | fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
574 | |||
575 | sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); | 571 | sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); |
576 | 572 | ||
577 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 573 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 0e6c9f595d75..20e3e53ce6ea 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h | |||
@@ -131,7 +131,6 @@ struct fsl_sai { | |||
131 | struct clk *bus_clk; | 131 | struct clk *bus_clk; |
132 | struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; | 132 | struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; |
133 | 133 | ||
134 | bool big_endian_regs; | ||
135 | bool big_endian_data; | 134 | bool big_endian_data; |
136 | bool is_dsp_mode; | 135 | bool is_dsp_mode; |
137 | bool sai_on_imx; | 136 | bool sai_on_imx; |
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 70acfe4a9bd5..ae4e408810ec 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c | |||
@@ -1040,7 +1040,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) | |||
1040 | } | 1040 | } |
1041 | } | 1041 | } |
1042 | 1042 | ||
1043 | static struct regmap_config fsl_spdif_regmap_config = { | 1043 | static const struct regmap_config fsl_spdif_regmap_config = { |
1044 | .reg_bits = 32, | 1044 | .reg_bits = 32, |
1045 | .reg_stride = 4, | 1045 | .reg_stride = 4, |
1046 | .val_bits = 32, | 1046 | .val_bits = 32, |
@@ -1184,9 +1184,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) | |||
1184 | memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); | 1184 | memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); |
1185 | spdif_priv->cpu_dai_drv.name = spdif_priv->name; | 1185 | spdif_priv->cpu_dai_drv.name = spdif_priv->name; |
1186 | 1186 | ||
1187 | if (of_property_read_bool(np, "big-endian")) | ||
1188 | fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
1189 | |||
1190 | /* Get the addresses and IRQ */ | 1187 | /* Get the addresses and IRQ */ |
1191 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1188 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1192 | regs = devm_ioremap_resource(&pdev->dev, res); | 1189 | regs = devm_ioremap_resource(&pdev->dev, res); |
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c new file mode 100644 index 000000000000..653e66d150c8 --- /dev/null +++ b/sound/soc/fsl/imx-es8328.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Freescale Semiconductor, Inc. | ||
3 | * Copyright 2012 Linaro Ltd. | ||
4 | * | ||
5 | * The code contained herein is licensed under the GNU General Public | ||
6 | * License. You may obtain a copy of the GNU General Public License | ||
7 | * Version 2 or later at the following locations: | ||
8 | * | ||
9 | * http://www.opensource.org/licenses/gpl-license.html | ||
10 | * http://www.gnu.org/copyleft/gpl.html | ||
11 | */ | ||
12 | |||
13 | #include <linux/gpio.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/of_gpio.h> | ||
19 | #include <sound/soc.h> | ||
20 | #include <sound/jack.h> | ||
21 | |||
22 | #include "imx-audmux.h" | ||
23 | |||
24 | #define DAI_NAME_SIZE 32 | ||
25 | #define MUX_PORT_MAX 7 | ||
26 | |||
27 | struct imx_es8328_data { | ||
28 | struct device *dev; | ||
29 | struct snd_soc_dai_link dai; | ||
30 | struct snd_soc_card card; | ||
31 | char codec_dai_name[DAI_NAME_SIZE]; | ||
32 | char platform_name[DAI_NAME_SIZE]; | ||
33 | int jack_gpio; | ||
34 | }; | ||
35 | |||
36 | static struct snd_soc_jack_gpio headset_jack_gpios[] = { | ||
37 | { | ||
38 | .gpio = -1, | ||
39 | .name = "headset-gpio", | ||
40 | .report = SND_JACK_HEADSET, | ||
41 | .invert = 0, | ||
42 | .debounce_time = 200, | ||
43 | }, | ||
44 | }; | ||
45 | |||
46 | static struct snd_soc_jack headset_jack; | ||
47 | |||
48 | static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) | ||
49 | { | ||
50 | struct imx_es8328_data *data = container_of(rtd->card, | ||
51 | struct imx_es8328_data, card); | ||
52 | int ret = 0; | ||
53 | |||
54 | /* Headphone jack detection */ | ||
55 | if (gpio_is_valid(data->jack_gpio)) { | ||
56 | ret = snd_soc_jack_new(rtd->codec, "Headphone", | ||
57 | SND_JACK_HEADPHONE | SND_JACK_BTN_0, | ||
58 | &headset_jack); | ||
59 | if (ret) | ||
60 | return ret; | ||
61 | |||
62 | headset_jack_gpios[0].gpio = data->jack_gpio; | ||
63 | ret = snd_soc_jack_add_gpios(&headset_jack, | ||
64 | ARRAY_SIZE(headset_jack_gpios), | ||
65 | headset_jack_gpios); | ||
66 | } | ||
67 | |||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { | ||
72 | SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
73 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
74 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
75 | SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), | ||
76 | }; | ||
77 | |||
78 | static int imx_es8328_probe(struct platform_device *pdev) | ||
79 | { | ||
80 | struct device_node *np = pdev->dev.of_node; | ||
81 | struct device_node *ssi_np, *codec_np; | ||
82 | struct platform_device *ssi_pdev; | ||
83 | struct imx_es8328_data *data; | ||
84 | u32 int_port, ext_port; | ||
85 | int ret; | ||
86 | struct device *dev = &pdev->dev; | ||
87 | |||
88 | ret = of_property_read_u32(np, "mux-int-port", &int_port); | ||
89 | if (ret) { | ||
90 | dev_err(dev, "mux-int-port missing or invalid\n"); | ||
91 | goto fail; | ||
92 | } | ||
93 | if (int_port > MUX_PORT_MAX || int_port == 0) { | ||
94 | dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", | ||
95 | MUX_PORT_MAX); | ||
96 | goto fail; | ||
97 | } | ||
98 | |||
99 | ret = of_property_read_u32(np, "mux-ext-port", &ext_port); | ||
100 | if (ret) { | ||
101 | dev_err(dev, "mux-ext-port missing or invalid\n"); | ||
102 | goto fail; | ||
103 | } | ||
104 | if (ext_port > MUX_PORT_MAX || ext_port == 0) { | ||
105 | dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n", | ||
106 | MUX_PORT_MAX); | ||
107 | goto fail; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * The port numbering in the hardware manual starts at 1, while | ||
112 | * the audmux API expects it starts at 0. | ||
113 | */ | ||
114 | int_port--; | ||
115 | ext_port--; | ||
116 | ret = imx_audmux_v2_configure_port(int_port, | ||
117 | IMX_AUDMUX_V2_PTCR_SYN | | ||
118 | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | | ||
119 | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | | ||
120 | IMX_AUDMUX_V2_PTCR_TFSDIR | | ||
121 | IMX_AUDMUX_V2_PTCR_TCLKDIR, | ||
122 | IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); | ||
123 | if (ret) { | ||
124 | dev_err(dev, "audmux internal port setup failed\n"); | ||
125 | return ret; | ||
126 | } | ||
127 | ret = imx_audmux_v2_configure_port(ext_port, | ||
128 | IMX_AUDMUX_V2_PTCR_SYN, | ||
129 | IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); | ||
130 | if (ret) { | ||
131 | dev_err(dev, "audmux external port setup failed\n"); | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); | ||
136 | codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); | ||
137 | if (!ssi_np || !codec_np) { | ||
138 | dev_err(dev, "phandle missing or invalid\n"); | ||
139 | ret = -EINVAL; | ||
140 | goto fail; | ||
141 | } | ||
142 | |||
143 | ssi_pdev = of_find_device_by_node(ssi_np); | ||
144 | if (!ssi_pdev) { | ||
145 | dev_err(dev, "failed to find SSI platform device\n"); | ||
146 | ret = -EINVAL; | ||
147 | goto fail; | ||
148 | } | ||
149 | |||
150 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
151 | if (!data) { | ||
152 | ret = -ENOMEM; | ||
153 | goto fail; | ||
154 | } | ||
155 | |||
156 | data->dev = dev; | ||
157 | |||
158 | data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0); | ||
159 | |||
160 | data->dai.name = "hifi"; | ||
161 | data->dai.stream_name = "hifi"; | ||
162 | data->dai.codec_dai_name = "es8328-hifi-analog"; | ||
163 | data->dai.codec_of_node = codec_np; | ||
164 | data->dai.cpu_of_node = ssi_np; | ||
165 | data->dai.platform_of_node = ssi_np; | ||
166 | data->dai.init = &imx_es8328_dai_init; | ||
167 | data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
168 | SND_SOC_DAIFMT_CBM_CFM; | ||
169 | |||
170 | data->card.dev = dev; | ||
171 | data->card.dapm_widgets = imx_es8328_dapm_widgets; | ||
172 | data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); | ||
173 | ret = snd_soc_of_parse_card_name(&data->card, "model"); | ||
174 | if (ret) { | ||
175 | dev_err(dev, "Unable to parse card name\n"); | ||
176 | goto fail; | ||
177 | } | ||
178 | ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); | ||
179 | if (ret) { | ||
180 | dev_err(dev, "Unable to parse routing: %d\n", ret); | ||
181 | goto fail; | ||
182 | } | ||
183 | data->card.num_links = 1; | ||
184 | data->card.owner = THIS_MODULE; | ||
185 | data->card.dai_link = &data->dai; | ||
186 | |||
187 | ret = snd_soc_register_card(&data->card); | ||
188 | if (ret) { | ||
189 | dev_err(dev, "Unable to register: %d\n", ret); | ||
190 | goto fail; | ||
191 | } | ||
192 | |||
193 | platform_set_drvdata(pdev, data); | ||
194 | fail: | ||
195 | of_node_put(ssi_np); | ||
196 | of_node_put(codec_np); | ||
197 | |||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | static int imx_es8328_remove(struct platform_device *pdev) | ||
202 | { | ||
203 | struct imx_es8328_data *data = platform_get_drvdata(pdev); | ||
204 | |||
205 | snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios), | ||
206 | headset_jack_gpios); | ||
207 | |||
208 | snd_soc_unregister_card(&data->card); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static const struct of_device_id imx_es8328_dt_ids[] = { | ||
214 | { .compatible = "fsl,imx-audio-es8328", }, | ||
215 | { /* sentinel */ } | ||
216 | }; | ||
217 | MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); | ||
218 | |||
219 | static struct platform_driver imx_es8328_driver = { | ||
220 | .driver = { | ||
221 | .name = "imx-es8328", | ||
222 | .of_match_table = imx_es8328_dt_ids, | ||
223 | }, | ||
224 | .probe = imx_es8328_probe, | ||
225 | .remove = imx_es8328_remove, | ||
226 | }; | ||
227 | module_platform_driver(imx_es8328_driver); | ||
228 | |||
229 | MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
230 | MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver"); | ||
231 | MODULE_LICENSE("GPL v2"); | ||
232 | MODULE_ALIAS("platform:imx-audio-es8328"); | ||