mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
Merge branch 'asoc-5.6' into asoc-next
This commit is contained in:
commit
a7196caf83
@ -17,6 +17,9 @@ Required properties:
|
||||
* "arb" : memory ARB line (required)
|
||||
* "rst" : dedicated device reset line (optional)
|
||||
- #sound-dai-cells: must be 0.
|
||||
- amlogic,fifo-depth: The size of the controller's fifo in bytes. This
|
||||
is useful for determining certain configuration such
|
||||
as the flush threshold of the fifo
|
||||
|
||||
Example of FRDDR A on the A113 SoC:
|
||||
|
||||
@ -27,4 +30,5 @@ frddr_a: audio-controller@1c0 {
|
||||
interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&clkc_audio AUD_CLKID_FRDDR_A>;
|
||||
resets = <&arb AXG_ARB_FRDDR_A>;
|
||||
fifo-depth = <512>;
|
||||
};
|
||||
|
@ -8,7 +8,12 @@ three substreams within totally 10 channels.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains "fsl,imx35-asrc" or "fsl,imx53-asrc".
|
||||
- compatible : Compatible list, should contain one of the following
|
||||
compatibles:
|
||||
"fsl,imx35-asrc",
|
||||
"fsl,imx53-asrc",
|
||||
"fsl,imx8qm-asrc",
|
||||
"fsl,imx8qxp-asrc",
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
@ -35,6 +40,11 @@ Required properties:
|
||||
|
||||
- fsl,asrc-width : Defines a mutual sample width used by DPCM Back Ends.
|
||||
|
||||
- fsl,asrc-clk-map : Defines clock map used in driver. which is required
|
||||
by imx8qm/imx8qxp platform
|
||||
<0> - select the map for asrc0 in imx8qm/imx8qxp
|
||||
<1> - select the map for asrc1 in imx8qm/imx8qxp
|
||||
|
||||
Optional properties:
|
||||
|
||||
- big-endian : If this property is absent, the little endian mode
|
||||
|
@ -1,10 +1,16 @@
|
||||
GTM601 UMTS modem audio interface CODEC
|
||||
|
||||
This device has no configuration interface. Sample rate is fixed - 8kHz.
|
||||
This device has no configuration interface. The sample rate and channels are
|
||||
based on the compatible string
|
||||
"option,gtm601" = 8kHz mono
|
||||
"broadmobi,bm818" = 48KHz stereo
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "option,gtm601"
|
||||
- compatible : one of
|
||||
"option,gtm601"
|
||||
"broadmobi,bm818"
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
55
Documentation/devicetree/bindings/sound/ingenic,codec.yaml
Normal file
55
Documentation/devicetree/bindings/sound/ingenic,codec.yaml
Normal file
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/ingenic,codec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ingenic JZ47xx internal codec DT bindings
|
||||
|
||||
maintainers:
|
||||
- Paul Cercueil <paul@crapouillou.net>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^audio-codec@.*'
|
||||
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: ingenic,jz4770-codec
|
||||
- const: ingenic,jz4725b-codec
|
||||
- const: ingenic,jz4740-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aic
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#sound-dai-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/jz4740-cgu.h>
|
||||
codec: audio-codec@10020080 {
|
||||
compatible = "ingenic,jz4740-codec";
|
||||
reg = <0x10020080 0x8>;
|
||||
#sound-dai-cells = <0>;
|
||||
clocks = <&cgu JZ4740_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
||||
|
||||
...
|
@ -1,20 +0,0 @@
|
||||
Ingenic JZ4725B codec controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "ingenic,jz4725b-codec"
|
||||
- reg : codec registers location and length
|
||||
- clocks : phandle to the AIC clock.
|
||||
- clock-names: must be set to "aic".
|
||||
- #sound-dai-cells: Must be set to 0.
|
||||
|
||||
Example:
|
||||
|
||||
codec: audio-codec@100200a4 {
|
||||
compatible = "ingenic,jz4725b-codec";
|
||||
reg = <0x100200a4 0x8>;
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&cgu JZ4725B_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
Ingenic JZ4740 codec controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "ingenic,jz4740-codec"
|
||||
- reg : codec registers location and length
|
||||
- clocks : phandle to the AIC clock.
|
||||
- clock-names: must be set to "aic".
|
||||
- #sound-dai-cells: Must be set to 0.
|
||||
|
||||
Example:
|
||||
|
||||
codec: audio-codec@10020080 {
|
||||
compatible = "ingenic,jz4740-codec";
|
||||
reg = <0x10020080 0x8>;
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
@ -5,7 +5,10 @@ This binding describes the SDM845 sound card, which uses qdsp for audio.
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must be "qcom,sdm845-sndcard"
|
||||
Definition: must be one of this
|
||||
"qcom,sdm845-sndcard"
|
||||
"qcom,db845c-sndcard"
|
||||
"lenovo,yoga-c630-sndcard"
|
||||
|
||||
- audio-routing:
|
||||
Usage: Optional
|
||||
|
175
Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
Normal file
175
Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
Normal file
@ -0,0 +1,175 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/qcom,wcd934x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Bindings for Qualcomm WCD9340/WCD9341 Audio Codec
|
||||
|
||||
maintainers:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
|
||||
description: |
|
||||
Qualcomm WCD9340/WCD9341 Codec is a standalone Hi-Fi audio codec IC.
|
||||
It has in-built Soundwire controller, pin controller, interrupt mux and
|
||||
supports both I2S/I2C and SLIMbus audio interfaces.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: slim217,250
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO spec for reset line to use
|
||||
maxItems: 1
|
||||
|
||||
slim-ifc-dev: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: extclk
|
||||
|
||||
vdd-buck-supply:
|
||||
description: A reference to the 1.8V buck supply
|
||||
|
||||
vdd-buck-sido-supply:
|
||||
description: A reference to the 1.8V SIDO buck supply
|
||||
|
||||
vdd-rx-supply:
|
||||
description: A reference to the 1.8V rx supply
|
||||
|
||||
vdd-tx-supply:
|
||||
description: A reference to the 1.8V tx supply
|
||||
|
||||
vdd-vbat-supply:
|
||||
description: A reference to the vbat supply
|
||||
|
||||
vdd-io-supply:
|
||||
description: A reference to the 1.8V I/O supply
|
||||
|
||||
vdd-micbias-supply:
|
||||
description: A reference to the micbias supply
|
||||
|
||||
qcom,micbias1-microvolt:
|
||||
description: micbias1 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias2-microvolt:
|
||||
description: micbias2 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias3-microvolt:
|
||||
description: micbias3 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias4-microvolt:
|
||||
description: micbias4 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
clock-output-names:
|
||||
const: mclk
|
||||
|
||||
clock-frequency:
|
||||
description: Clock frequency of output clk in Hz
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
gpio@42:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: ../gpio/qcom,wcd934x-gpio.yaml#
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+$":
|
||||
type: object
|
||||
description: |
|
||||
WCD934x subnode for each slave devices. Bindings of each subnodes
|
||||
depends on the specific driver providing the functionality and
|
||||
documented in their respective bindings.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- slim-ifc-dev
|
||||
- interrupts
|
||||
- interrupt-controller
|
||||
- clock-frequency
|
||||
- clock-output-names
|
||||
- qcom,micbias1-microvolt
|
||||
- qcom,micbias2-microvolt
|
||||
- qcom,micbias3-microvolt
|
||||
- qcom,micbias4-microvolt
|
||||
- "#interrupt-cells"
|
||||
- "#clock-cells"
|
||||
- "#sound-dai-cells"
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
examples:
|
||||
- |
|
||||
codec@1,0{
|
||||
compatible = "slim217,250";
|
||||
reg = <1 0>;
|
||||
reset-gpios = <&tlmm 64 0>;
|
||||
slim-ifc-dev = <&wcd9340_ifd>;
|
||||
#sound-dai-cells = <1>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <54 4>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <9600000>;
|
||||
clock-output-names = "mclk";
|
||||
qcom,micbias1-microvolt = <1800000>;
|
||||
qcom,micbias2-microvolt = <1800000>;
|
||||
qcom,micbias3-microvolt = <1800000>;
|
||||
qcom,micbias4-microvolt = <1800000>;
|
||||
clock-names = "extclk";
|
||||
clocks = <&rpmhcc 2>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
gpio@42 {
|
||||
compatible = "qcom,wcd9340-gpio";
|
||||
reg = <0x42 0x2>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
68
Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
Normal file
68
Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
Normal file
@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/qcom,wsa881x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Bindings for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier
|
||||
|
||||
maintainers:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
|
||||
description: |
|
||||
WSA8810 is a class-D smart speaker amplifier and WSA8815
|
||||
is a high-output power class-D smart speaker amplifier.
|
||||
Their primary operating mode uses a SoundWire digital audio
|
||||
interface. This binding is for SoundWire interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sdw10217201000
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
powerdown-gpios:
|
||||
description: GPIO spec for Powerdown/Shutdown line to use
|
||||
maxItems: 1
|
||||
|
||||
'#thermal-sensor-cells':
|
||||
const: 0
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- powerdown-gpios
|
||||
- "#thermal-sensor-cells"
|
||||
- "#sound-dai-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soundwire@c2d0000 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0c2d0000 0x2000>;
|
||||
|
||||
speaker@0,1 {
|
||||
compatible = "sdw10217201000";
|
||||
reg = <0 1>;
|
||||
powerdown-gpios = <&wcdpinctrl 2 0>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
||||
|
||||
speaker@0,2 {
|
||||
compatible = "sdw10217201000";
|
||||
reg = <0 2>;
|
||||
powerdown-gpios = <&wcdpinctrl 2 0>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
17
Documentation/devicetree/bindings/sound/rt1015.txt
Normal file
17
Documentation/devicetree/bindings/sound/rt1015.txt
Normal file
@ -0,0 +1,17 @@
|
||||
RT1015 Mono Class D Audio Amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt1015".
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
rt1015: codec@28 {
|
||||
compatible = "realtek,rt1015";
|
||||
reg = <0x28>;
|
||||
};
|
@ -10,6 +10,10 @@ Required properties:
|
||||
|
||||
- interrupts : The CODEC's interrupt output.
|
||||
|
||||
- avdd-supply: Power supply for AVDD, providing 1.8V.
|
||||
|
||||
- cpvdd-supply: Power supply for CPVDD, providing 3.5V.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- hp-detect-gpios:
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/**
|
||||
* regulator_is_enabled_regmap - standard is_enabled() for regmap users
|
||||
*
|
||||
@ -881,3 +883,15 @@ void regulator_bulk_set_supply_names(struct regulator_bulk_data *consumers,
|
||||
consumers[i].supply = supply_names[i];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regulator_bulk_set_supply_names);
|
||||
|
||||
/**
|
||||
* regulator_is_equal - test whether two regulators are the same
|
||||
*
|
||||
* @reg1: first regulator to operate on
|
||||
* @reg2: second regulator to operate on
|
||||
*/
|
||||
bool regulator_is_equal(struct regulator *reg1, struct regulator *reg2)
|
||||
{
|
||||
return reg1->rdev == reg2->rdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regulator_is_equal);
|
||||
|
@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
|
||||
intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
|
||||
}
|
||||
|
||||
static int intel_config_stream(struct sdw_intel *sdw,
|
||||
static int intel_params_stream(struct sdw_intel *sdw,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
struct snd_pcm_hw_params *hw_params, int link_id)
|
||||
struct snd_pcm_hw_params *hw_params,
|
||||
int link_id, int alh_stream_id)
|
||||
{
|
||||
struct sdw_intel_link_res *res = sdw->res;
|
||||
struct sdw_intel_stream_params_data params_data;
|
||||
|
||||
if (res->ops && res->ops->config_stream && res->arg)
|
||||
return res->ops->config_stream(res->arg,
|
||||
substream, dai, hw_params, link_id);
|
||||
params_data.substream = substream;
|
||||
params_data.dai = dai;
|
||||
params_data.hw_params = hw_params;
|
||||
params_data.link_id = link_id;
|
||||
params_data.alh_stream_id = alh_stream_id;
|
||||
|
||||
if (res->ops && res->ops->params_stream && res->dev)
|
||||
return res->ops->params_stream(res->dev,
|
||||
¶ms_data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
|
||||
/* Inform DSP about PDI stream number */
|
||||
ret = intel_config_stream(sdw, substream, dai, params,
|
||||
ret = intel_params_stream(sdw, substream, dai, params,
|
||||
sdw->instance,
|
||||
pdi->intel_alh_id);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
@ -5,23 +5,26 @@
|
||||
#define __SDW_INTEL_LOCAL_H
|
||||
|
||||
/**
|
||||
* struct sdw_intel_link_res - Soundwire link resources
|
||||
* struct sdw_intel_link_res - Soundwire Intel link resource structure,
|
||||
* typically populated by the controller driver.
|
||||
* @pdev: platform_device
|
||||
* @mmio_base: mmio base of SoundWire registers
|
||||
* @registers: Link IO registers base
|
||||
* @shim: Audio shim pointer
|
||||
* @alh: ALH (Audio Link Hub) pointer
|
||||
* @irq: Interrupt line
|
||||
* @ops: Shim callback ops
|
||||
* @arg: Shim callback ops argument
|
||||
*
|
||||
* This is set as pdata for each link instance.
|
||||
* @dev: device implementing hw_params and free callbacks
|
||||
*/
|
||||
struct sdw_intel_link_res {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *mmio_base; /* not strictly needed, useful for debug */
|
||||
void __iomem *registers;
|
||||
void __iomem *shim;
|
||||
void __iomem *alh;
|
||||
int irq;
|
||||
const struct sdw_intel_ops *ops;
|
||||
void *arg;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
#endif /* __SDW_INTEL_LOCAL_H */
|
||||
|
@ -27,19 +27,9 @@ static int link_mask;
|
||||
module_param_named(sdw_link_mask, link_mask, int, 0444);
|
||||
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
|
||||
|
||||
struct sdw_link_data {
|
||||
struct sdw_intel_link_res res;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
struct sdw_intel_ctx {
|
||||
int count;
|
||||
struct sdw_link_data *links;
|
||||
};
|
||||
|
||||
static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
|
||||
{
|
||||
struct sdw_link_data *link = ctx->links;
|
||||
struct sdw_intel_link_res *link = ctx->links;
|
||||
int i;
|
||||
|
||||
if (!link)
|
||||
@ -62,7 +52,7 @@ static struct sdw_intel_ctx
|
||||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
struct sdw_link_data *link;
|
||||
struct sdw_intel_link_res *link;
|
||||
struct sdw_intel_ctx *ctx;
|
||||
struct acpi_device *adev;
|
||||
int ret, i;
|
||||
@ -123,14 +113,13 @@ static struct sdw_intel_ctx
|
||||
continue;
|
||||
}
|
||||
|
||||
link->res.irq = res->irq;
|
||||
link->res.registers = res->mmio_base + SDW_LINK_BASE
|
||||
link->registers = res->mmio_base + SDW_LINK_BASE
|
||||
+ (SDW_LINK_SIZE * i);
|
||||
link->res.shim = res->mmio_base + SDW_SHIM_BASE;
|
||||
link->res.alh = res->mmio_base + SDW_ALH_BASE;
|
||||
link->shim = res->mmio_base + SDW_SHIM_BASE;
|
||||
link->alh = res->mmio_base + SDW_ALH_BASE;
|
||||
|
||||
link->res.ops = res->ops;
|
||||
link->res.arg = res->arg;
|
||||
link->ops = res->ops;
|
||||
link->dev = res->dev;
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
@ -138,8 +127,6 @@ static struct sdw_intel_ctx
|
||||
pdevinfo.name = "int-sdw";
|
||||
pdevinfo.id = i;
|
||||
pdevinfo.fwnode = acpi_fwnode_handle(adev);
|
||||
pdevinfo.data = &link->res;
|
||||
pdevinfo.size_data = sizeof(link->res);
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
@ -216,7 +203,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
|
||||
|
||||
return sdw_intel_add_controller(res);
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_intel_init);
|
||||
|
||||
/**
|
||||
* sdw_intel_exit() - SoundWire Intel exit
|
||||
@ -224,10 +210,8 @@ EXPORT_SYMBOL(sdw_intel_init);
|
||||
*
|
||||
* Delete the controller instances created and cleanup
|
||||
*/
|
||||
void sdw_intel_exit(void *arg)
|
||||
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
|
||||
{
|
||||
struct sdw_intel_ctx *ctx = arg;
|
||||
|
||||
sdw_intel_cleanup_pdev(ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
@ -287,6 +287,8 @@ void regulator_bulk_set_supply_names(struct regulator_bulk_data *consumers,
|
||||
const char *const *supply_names,
|
||||
unsigned int num_supplies);
|
||||
|
||||
bool regulator_is_equal(struct regulator *reg1, struct regulator *reg2);
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
@ -593,6 +595,11 @@ regulator_bulk_set_supply_names(struct regulator_bulk_data *consumers,
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool
|
||||
regulator_is_equal(struct regulator *reg1, struct regulator *reg2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int regulator_set_voltage_triplet(struct regulator *regulator,
|
||||
|
@ -547,6 +547,20 @@ struct sdw_slave_ops {
|
||||
* @node: node for bus list
|
||||
* @port_ready: Port ready completion flag for each Slave port
|
||||
* @dev_num: Device Number assigned by Bus
|
||||
* @probed: boolean tracking driver state
|
||||
* @probe_complete: completion utility to control potential races
|
||||
* on startup between driver probe/initialization and SoundWire
|
||||
* Slave state changes/implementation-defined interrupts
|
||||
* @enumeration_complete: completion utility to control potential races
|
||||
* on startup between device enumeration and read/write access to the
|
||||
* Slave device
|
||||
* @initialization_complete: completion utility to control potential races
|
||||
* on startup between device enumeration and settings being restored
|
||||
* @unattach_request: mask field to keep track why the Slave re-attached and
|
||||
* was re-initialized. This is useful to deal with potential race conditions
|
||||
* between the Master suspending and the codec resuming, and make sure that
|
||||
* when the Master triggered a reset the Slave is properly enumerated and
|
||||
* initialized
|
||||
*/
|
||||
struct sdw_slave {
|
||||
struct sdw_slave_id id;
|
||||
@ -561,6 +575,11 @@ struct sdw_slave {
|
||||
struct list_head node;
|
||||
struct completion *port_ready;
|
||||
u16 dev_num;
|
||||
bool probed;
|
||||
struct completion probe_complete;
|
||||
struct completion enumeration_complete;
|
||||
struct completion initialization_complete;
|
||||
u32 unattach_request;
|
||||
};
|
||||
|
||||
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
|
||||
|
@ -4,36 +4,174 @@
|
||||
#ifndef __SDW_INTEL_H
|
||||
#define __SDW_INTEL_H
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ops: Intel audio driver callback ops
|
||||
*
|
||||
* @config_stream: configure the stream with the hw_params
|
||||
* the first argument containing the context is mandatory
|
||||
* struct sdw_intel_stream_params_data: configuration passed during
|
||||
* the @params_stream callback, e.g. for interaction with DSP
|
||||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_ops {
|
||||
int (*config_stream)(void *arg, void *substream,
|
||||
void *dai, void *hw_params, int stream_num);
|
||||
struct sdw_intel_stream_params_data {
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
struct snd_pcm_hw_params *hw_params;
|
||||
int link_id;
|
||||
int alh_stream_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_res - Soundwire Intel resource structure
|
||||
* struct sdw_intel_stream_free_data: configuration passed during
|
||||
* the @free_stream callback, e.g. for interaction with DSP
|
||||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_stream_free_data {
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
int link_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ops: Intel audio driver callback ops
|
||||
*
|
||||
*/
|
||||
struct sdw_intel_ops {
|
||||
int (*params_stream)(struct device *dev,
|
||||
struct sdw_intel_stream_params_data *params_data);
|
||||
int (*free_stream)(struct device *dev,
|
||||
struct sdw_intel_stream_free_data *free_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_acpi_info - Soundwire Intel information found in ACPI tables
|
||||
* @handle: ACPI controller handle
|
||||
* @count: link count found with "sdw-master-count" property
|
||||
* @link_mask: bit-wise mask listing links enabled by BIOS menu
|
||||
*
|
||||
* this structure could be expanded to e.g. provide all the _ADR
|
||||
* information in case the link_mask is not sufficient to identify
|
||||
* platform capabilities.
|
||||
*/
|
||||
struct sdw_intel_acpi_info {
|
||||
acpi_handle handle;
|
||||
int count;
|
||||
u32 link_mask;
|
||||
};
|
||||
|
||||
struct sdw_intel_link_res;
|
||||
|
||||
/* Intel clock-stop/pm_runtime quirk definitions */
|
||||
|
||||
/*
|
||||
* Force the clock to remain on during pm_runtime suspend. This might
|
||||
* be needed if Slave devices do not have an alternate clock source or
|
||||
* if the latency requirements are very strict.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_NOT_ALLOWED BIT(0)
|
||||
|
||||
/*
|
||||
* Stop the bus during pm_runtime suspend. If set, a complete bus
|
||||
* reset and re-enumeration will be performed when the bus
|
||||
* restarts. This mode shall not be used if Slave devices can generate
|
||||
* in-band wakes.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_TEARDOWN BIT(1)
|
||||
|
||||
/*
|
||||
* Stop the bus during pm_suspend if Slaves are not wake capable
|
||||
* (e.g. speaker amplifiers). The clock-stop mode is typically
|
||||
* slightly higher power than when the IP is completely powered-off.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_WAKE_CAPABLE_ONLY BIT(2)
|
||||
|
||||
/*
|
||||
* Require a bus reset (and complete re-enumeration) when exiting
|
||||
* clock stop modes. This may be needed if the controller power was
|
||||
* turned off and all context lost. This quirk shall not be used if a
|
||||
* Slave device needs to remain enumerated and keep its context,
|
||||
* e.g. to provide the reasons for the wake, report acoustic events or
|
||||
* pass a history buffer.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3)
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ctx - context allocated by the controller
|
||||
* driver probe
|
||||
* @count: link count
|
||||
* @mmio_base: mmio base of SoundWire registers, only used to check
|
||||
* hardware capabilities after all power dependencies are settled.
|
||||
* @link_mask: bit-wise mask listing SoundWire links reported by the
|
||||
* Controller
|
||||
* @handle: ACPI parent handle
|
||||
* @links: information for each link (controller-specific and kept
|
||||
* opaque here)
|
||||
* @link_list: list to handle interrupts across all links
|
||||
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
|
||||
*/
|
||||
struct sdw_intel_ctx {
|
||||
int count;
|
||||
void __iomem *mmio_base;
|
||||
u32 link_mask;
|
||||
acpi_handle handle;
|
||||
struct sdw_intel_link_res *links;
|
||||
struct list_head link_list;
|
||||
struct mutex shim_lock; /* lock for access to shared SHIM registers */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_res - Soundwire Intel global resource structure,
|
||||
* typically populated by the DSP driver
|
||||
*
|
||||
* @count: link count
|
||||
* @mmio_base: mmio base of SoundWire registers
|
||||
* @irq: interrupt number
|
||||
* @handle: ACPI parent handle
|
||||
* @parent: parent device
|
||||
* @ops: callback ops
|
||||
* @arg: callback arg
|
||||
* @dev: device implementing hwparams and free callbacks
|
||||
* @link_mask: bit-wise mask listing links selected by the DSP driver
|
||||
* This mask may be a subset of the one reported by the controller since
|
||||
* machine-specific quirks are handled in the DSP driver.
|
||||
* @clock_stop_quirks: mask array of possible behaviors requested by the
|
||||
* DSP driver. The quirks are common for all links for now.
|
||||
*/
|
||||
struct sdw_intel_res {
|
||||
int count;
|
||||
void __iomem *mmio_base;
|
||||
int irq;
|
||||
acpi_handle handle;
|
||||
struct device *parent;
|
||||
const struct sdw_intel_ops *ops;
|
||||
void *arg;
|
||||
struct device *dev;
|
||||
u32 link_mask;
|
||||
u32 clock_stop_quirks;
|
||||
};
|
||||
|
||||
void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res);
|
||||
void sdw_intel_exit(void *arg);
|
||||
/*
|
||||
* On Intel platforms, the SoundWire IP has dependencies on power
|
||||
* rails shared with the DSP, and the initialization steps are split
|
||||
* in three. First an ACPI scan to check what the firmware describes
|
||||
* in DSDT tables, then an allocation step (with no hardware
|
||||
* configuration but with all the relevant devices created) and last
|
||||
* the actual hardware configuration. The final stage is a global
|
||||
* interrupt enable which is controlled by the DSP driver. Splitting
|
||||
* these phases helps simplify the boot flow and make early decisions
|
||||
* on e.g. which machine driver to select (I2S mode, HDaudio or
|
||||
* SoundWire).
|
||||
*/
|
||||
int sdw_intel_acpi_scan(acpi_handle *parent_handle,
|
||||
struct sdw_intel_acpi_info *info);
|
||||
|
||||
void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx);
|
||||
|
||||
struct sdw_intel_ctx *
|
||||
sdw_intel_probe(struct sdw_intel_res *res);
|
||||
|
||||
int sdw_intel_startup(struct sdw_intel_ctx *ctx);
|
||||
|
||||
void sdw_intel_exit(struct sdw_intel_ctx *ctx);
|
||||
|
||||
void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable);
|
||||
|
||||
irqreturn_t sdw_intel_thread(int irq, void *dev_id);
|
||||
|
||||
#endif
|
||||
|
@ -31,6 +31,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
|
||||
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[];
|
||||
|
||||
/*
|
||||
* generic table used for HDA codec-based platforms, possibly with
|
||||
* additional ACPI-enumerated codecs
|
||||
|
@ -61,6 +61,8 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
|
||||
* @platform: string used for HDaudio codec support
|
||||
* @codec_mask: used for HDAudio support
|
||||
* @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
|
||||
* @link_mask: links enabled on the board
|
||||
* @links: array of link _ADR descriptors, null terminated
|
||||
*/
|
||||
struct snd_soc_acpi_mach_params {
|
||||
u32 acpi_ipc_irq_index;
|
||||
@ -68,6 +70,23 @@ struct snd_soc_acpi_mach_params {
|
||||
u32 codec_mask;
|
||||
u32 dmic_num;
|
||||
bool common_hdmi_codec_drv;
|
||||
u32 link_mask;
|
||||
const struct snd_soc_acpi_link_adr *links;
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_acpi_link_adr: ACPI-based list of _ADR, with a variable
|
||||
* number of devices per link
|
||||
*
|
||||
* @mask: one bit set indicates the link this list applies to
|
||||
* @num_adr: ARRAY_SIZE of adr
|
||||
* @adr: array of _ADR (represented as u64).
|
||||
*/
|
||||
|
||||
struct snd_soc_acpi_link_adr {
|
||||
const u32 mask;
|
||||
const u32 num_adr;
|
||||
const u64 *adr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -78,6 +97,7 @@ struct snd_soc_acpi_mach_params {
|
||||
*
|
||||
* @id: ACPI ID (usually the codec's) used to find a matching machine driver.
|
||||
* @link_mask: describes required board layout, e.g. for SoundWire.
|
||||
* @links: array of link _ADR descriptors, null terminated.
|
||||
* @drv_name: machine driver name
|
||||
* @fw_filename: firmware file name. Used when SOF is not enabled.
|
||||
* @board: board name
|
||||
@ -94,6 +114,7 @@ struct snd_soc_acpi_mach_params {
|
||||
struct snd_soc_acpi_mach {
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
const u32 link_mask;
|
||||
const struct snd_soc_acpi_link_adr *links;
|
||||
const char *drv_name;
|
||||
const char *fw_filename;
|
||||
const char *board;
|
||||
|
@ -286,8 +286,6 @@ struct snd_soc_dai_driver {
|
||||
/* DAI driver callbacks */
|
||||
int (*probe)(struct snd_soc_dai *dai);
|
||||
int (*remove)(struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
/* compress dai */
|
||||
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
|
||||
/* Optional Callback used at pcm creation*/
|
||||
@ -304,7 +302,6 @@ struct snd_soc_dai_driver {
|
||||
unsigned int symmetric_rates:1;
|
||||
unsigned int symmetric_channels:1;
|
||||
unsigned int symmetric_samplebits:1;
|
||||
unsigned int bus_control:1; /* DAI is also used for the control bus */
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
|
@ -392,6 +392,8 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_soc_dapm_put_enum_double_locked(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
|
||||
@ -434,6 +436,7 @@ void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm);
|
||||
/* dapm events */
|
||||
void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
|
||||
int event);
|
||||
void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream);
|
||||
void snd_soc_dapm_shutdown(struct snd_soc_card *card);
|
||||
|
||||
/* external DAPM widget events */
|
||||
|
@ -464,10 +464,8 @@ static inline int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
|
||||
|
||||
void snd_soc_disconnect_sync(struct device *dev);
|
||||
|
||||
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
|
||||
const char *dai_link, int stream);
|
||||
struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
|
||||
const char *dai_link);
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
|
||||
bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd);
|
||||
void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream);
|
||||
@ -738,19 +736,9 @@ struct snd_soc_compr_ops {
|
||||
int (*trigger)(struct snd_compr_stream *);
|
||||
};
|
||||
|
||||
struct snd_soc_rtdcom_list {
|
||||
struct snd_soc_component *component;
|
||||
struct list_head list; /* rtd::component_list */
|
||||
};
|
||||
struct snd_soc_component*
|
||||
snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
|
||||
const char *driver_name);
|
||||
#define for_each_rtd_components(rtd, rtdcom, _component) \
|
||||
for (rtdcom = list_first_entry(&(rtd)->component_list, \
|
||||
typeof(*rtdcom), list); \
|
||||
(&rtdcom->list != &(rtd)->component_list) && \
|
||||
(_component = rtdcom->component); \
|
||||
rtdcom = list_next_entry(rtdcom, list))
|
||||
|
||||
struct snd_soc_dai_link_component {
|
||||
const char *name;
|
||||
@ -852,7 +840,6 @@ struct snd_soc_dai_link {
|
||||
/* Do not create a PCM for this DAI link (Backend link) */
|
||||
unsigned int ignore:1;
|
||||
|
||||
struct list_head list; /* DAI link list of the soc card */
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj; /* For topology */
|
||||
#endif
|
||||
@ -952,6 +939,7 @@ struct snd_soc_dai_link {
|
||||
#define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, }
|
||||
#define COMP_PLATFORM(_name) { .name = _name }
|
||||
#define COMP_AUX(_name) { .name = _name }
|
||||
#define COMP_CODEC_CONF(_name) { .name = _name }
|
||||
#define COMP_DUMMY() { .name = "snd-soc-dummy", .dai_name = "snd-soc-dummy-dai", }
|
||||
|
||||
extern struct snd_soc_dai_link_component null_dailink_component[0];
|
||||
@ -962,8 +950,7 @@ struct snd_soc_codec_conf {
|
||||
* specify device either by device name, or by
|
||||
* DT/OF node, but not both.
|
||||
*/
|
||||
const char *dev_name;
|
||||
struct device_node *of_node;
|
||||
struct snd_soc_dai_link_component dlc;
|
||||
|
||||
/*
|
||||
* optional map of kcontrol, widget and path name prefixes that are
|
||||
@ -989,7 +976,9 @@ struct snd_soc_card {
|
||||
const char *long_name;
|
||||
const char *driver_name;
|
||||
const char *components;
|
||||
#ifdef CONFIG_DMI
|
||||
char dmi_longname[80];
|
||||
#endif /* CONFIG_DMI */
|
||||
char topology_shortname[32];
|
||||
|
||||
struct device *dev;
|
||||
@ -1037,7 +1026,6 @@ struct snd_soc_card {
|
||||
/* CPU <--> Codec DAI links */
|
||||
struct snd_soc_dai_link *dai_link; /* predefined links only */
|
||||
int num_links; /* predefined links only */
|
||||
struct list_head dai_link_list; /* all links */
|
||||
|
||||
struct list_head rtd_list;
|
||||
int num_rtd;
|
||||
@ -1107,11 +1095,6 @@ struct snd_soc_card {
|
||||
((i) < (card)->num_aux_devs) && ((aux) = &(card)->aux_dev[i]); \
|
||||
(i)++)
|
||||
|
||||
#define for_each_card_links(card, link) \
|
||||
list_for_each_entry(link, &(card)->dai_link_list, list)
|
||||
#define for_each_card_links_safe(card, link, _link) \
|
||||
list_for_each_entry_safe(link, _link, &(card)->dai_link_list, list)
|
||||
|
||||
#define for_each_card_rtds(card, rtd) \
|
||||
list_for_each_entry(rtd, &(card)->rtd_list, list)
|
||||
#define for_each_card_rtds_safe(card, rtd, _rtd) \
|
||||
@ -1157,12 +1140,18 @@ struct snd_soc_pcm_runtime {
|
||||
|
||||
unsigned int num; /* 0-based and monotonic increasing */
|
||||
struct list_head list; /* rtd list of the soc card */
|
||||
struct list_head component_list; /* list of connected components */
|
||||
|
||||
/* bit field */
|
||||
unsigned int pop_wait:1;
|
||||
unsigned int fe_compr:1; /* for Dynamic PCM */
|
||||
|
||||
int num_components;
|
||||
struct snd_soc_component *components[0]; /* CPU/Codec/Platform */
|
||||
};
|
||||
#define for_each_rtd_components(rtd, i, component) \
|
||||
for ((i) = 0; \
|
||||
((i) < rtd->num_components) && ((component) = rtd->components[i]);\
|
||||
(i)++)
|
||||
#define for_each_rtd_codec_dai(rtd, i, dai)\
|
||||
for ((i) = 0; \
|
||||
((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \
|
||||
@ -1170,6 +1159,7 @@ struct snd_soc_pcm_runtime {
|
||||
#define for_each_rtd_codec_dai_rollback(rtd, i, dai) \
|
||||
for (; ((--i) >= 0) && ((dai) = rtd->codec_dais[i]);)
|
||||
|
||||
void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd);
|
||||
|
||||
/* mixer control */
|
||||
struct soc_mixer_control {
|
||||
@ -1333,13 +1323,10 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link);
|
||||
|
||||
int snd_soc_add_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
void snd_soc_remove_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
|
||||
int id, const char *name,
|
||||
const char *stream_name);
|
||||
int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd);
|
||||
|
||||
struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
|
||||
struct snd_soc_dai_driver *dai_drv,
|
||||
@ -1409,11 +1396,6 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
|
||||
mutex_unlock(&dapm->card->dapm_mutex);
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg);
|
||||
|
||||
#include <sound/soc-component.h>
|
||||
|
||||
#endif
|
||||
|
@ -22,7 +22,6 @@ struct snd_sof_dsp_ops;
|
||||
*/
|
||||
struct snd_sof_pdata {
|
||||
const struct firmware *fw;
|
||||
const char *drv_name;
|
||||
const char *name;
|
||||
const char *platform;
|
||||
|
||||
@ -84,20 +83,18 @@ struct sof_dev_desc {
|
||||
const void *chip_info;
|
||||
|
||||
/* defaults for no codec mode */
|
||||
const char *nocodec_fw_filename;
|
||||
const char *nocodec_tplg_filename;
|
||||
|
||||
/* defaults paths for firmware and topology files */
|
||||
const char *default_fw_path;
|
||||
const char *default_tplg_path;
|
||||
|
||||
/* default firmware name */
|
||||
const char *default_fw_filename;
|
||||
|
||||
const struct snd_sof_dsp_ops *ops;
|
||||
const struct sof_arch_ops *arch_ops;
|
||||
};
|
||||
|
||||
int sof_nocodec_setup(struct device *dev,
|
||||
struct snd_sof_pdata *sof_pdata,
|
||||
struct snd_soc_acpi_mach *mach,
|
||||
const struct sof_dev_desc *desc,
|
||||
const struct snd_sof_dsp_ops *ops);
|
||||
#endif
|
||||
|
61
include/sound/sof/channel_map.h
Normal file
61
include/sound/sof/channel_map.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2019 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __IPC_CHANNEL_MAP_H__
|
||||
#define __IPC_CHANNEL_MAP_H__
|
||||
|
||||
#include <uapi/sound/sof/header.h>
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/**
|
||||
* \brief Channel map, specifies transformation of one-to-many or many-to-one.
|
||||
*
|
||||
* In case of one-to-many specifies how the output channels are computed out of
|
||||
* a single source channel,
|
||||
* in case of many-to-one specifies how a single target channel is computed
|
||||
* from a multichannel input stream.
|
||||
*
|
||||
* Channel index specifies position of the channel in the stream on the 'one'
|
||||
* side.
|
||||
*
|
||||
* Ext ID is the identifier of external part of the transformation. Depending
|
||||
* on the context, it may be pipeline ID, dai ID, ...
|
||||
*
|
||||
* Channel mask describes which channels are taken into account on the "many"
|
||||
* side. Bit[i] set to 1 means that i-th channel is used for computation
|
||||
* (either as source or as a target).
|
||||
*
|
||||
* Channel mask is followed by array of coefficients in Q2.30 format,
|
||||
* one per each channel set in the mask (left to right, LS bit set in the
|
||||
* mask corresponds to ch_coeffs[0]).
|
||||
*/
|
||||
struct sof_ipc_channel_map {
|
||||
uint32_t ch_index;
|
||||
uint32_t ext_id;
|
||||
uint32_t ch_mask;
|
||||
uint32_t reserved;
|
||||
int32_t ch_coeffs[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* \brief Complete map for each channel of a multichannel stream.
|
||||
*
|
||||
* num_ch_map Specifies number of items in the ch_map.
|
||||
* More than one transformation per a single channel is allowed (in case
|
||||
* multiple external entities are transformed).
|
||||
* A channel may be skipped in the transformation list, then it is filled
|
||||
* with 0's by the transformation function.
|
||||
*/
|
||||
struct sof_ipc_stream_map {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t num_ch_map;
|
||||
uint32_t reserved[3];
|
||||
struct sof_ipc_channel_map ch_map[0];
|
||||
} __packed;
|
||||
|
||||
#endif /* __IPC_CHANNEL_MAP_H__ */
|
@ -31,4 +31,24 @@ struct sof_ipc_dai_esai_params {
|
||||
uint16_t reserved2; /* alignment */
|
||||
} __packed;
|
||||
|
||||
/* SAI Configuration Request - SOF_IPC_DAI_SAI_CONFIG */
|
||||
struct sof_ipc_dai_sai_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
||||
/* MCLK */
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint32_t mclk_rate; /* MCLK frequency in Hz */
|
||||
uint32_t fsync_rate; /* FSYNC frequency in Hz */
|
||||
uint32_t bclk_rate; /* BCLK frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
} __packed;
|
||||
#endif
|
||||
|
@ -75,6 +75,7 @@ struct sof_ipc_dai_config {
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
struct sof_ipc_dai_alh_params alh;
|
||||
struct sof_ipc_dai_esai_params esai;
|
||||
struct sof_ipc_dai_sai_params sai;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
enum sof_ipc_ext_data {
|
||||
SOF_IPC_EXT_DMA_BUFFER = 0,
|
||||
SOF_IPC_EXT_WINDOW,
|
||||
SOF_IPC_EXT_CC_INFO,
|
||||
};
|
||||
|
||||
/* FW version - SOF_IPC_GLB_VERSION */
|
||||
@ -115,4 +116,18 @@ struct sof_ipc_window {
|
||||
struct sof_ipc_window_elem window[];
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_cc_version {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t micro;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
|
||||
char name[16]; /* null terminated compiler name */
|
||||
char optim[4]; /* null terminated compiler -O flag value */
|
||||
char desc[]; /* null terminated compiler description */
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
@ -36,6 +36,7 @@ enum sof_comp_type {
|
||||
SOF_COMP_KPB, /* A key phrase buffer component */
|
||||
SOF_COMP_SELECTOR, /**< channel selector component */
|
||||
SOF_COMP_DEMUX,
|
||||
SOF_COMP_ASRC, /**< Asynchronous sample rate converter */
|
||||
/* keep FILEREAD/FILEWRITE as the last ones */
|
||||
SOF_COMP_FILEREAD = 10000, /**< host test based file IO */
|
||||
SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */
|
||||
@ -147,6 +148,32 @@ struct sof_ipc_comp_src {
|
||||
uint32_t rate_mask; /**< SOF_RATE_ supported rates */
|
||||
} __packed;
|
||||
|
||||
/* generic ASRC component */
|
||||
struct sof_ipc_comp_asrc {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
/* either source or sink rate must be non zero */
|
||||
uint32_t source_rate; /**< Define fixed source rate or */
|
||||
/**< use 0 to indicate need to get */
|
||||
/**< the rate from stream */
|
||||
uint32_t sink_rate; /**< Define fixed sink rate or */
|
||||
/**< use 0 to indicate need to get */
|
||||
/**< the rate from stream */
|
||||
uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */
|
||||
/**< When 1 the ASRC tracks and */
|
||||
/**< compensates for drift. */
|
||||
uint32_t operation_mode; /**< push 0, pull 1, In push mode the */
|
||||
/**< ASRC consumes a defined number */
|
||||
/**< of frames at input, with varying */
|
||||
/**< number of frames at output. */
|
||||
/**< In pull mode the ASRC outputs */
|
||||
/**< a defined number of frames while */
|
||||
/**< number of input frames varies. */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* generic MUX component */
|
||||
struct sof_ipc_comp_mux {
|
||||
struct sof_ipc_comp comp;
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 11
|
||||
#define SOF_ABI_MINOR 12
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
@ -57,6 +57,12 @@
|
||||
#define SOF_TKN_SRC_RATE_IN 300
|
||||
#define SOF_TKN_SRC_RATE_OUT 301
|
||||
|
||||
/* ASRC */
|
||||
#define SOF_TKN_ASRC_RATE_IN 320
|
||||
#define SOF_TKN_ASRC_RATE_OUT 321
|
||||
#define SOF_TKN_ASRC_ASYNCHRONOUS_MODE 322
|
||||
#define SOF_TKN_ASRC_OPERATION_MODE 323
|
||||
|
||||
/* PCM */
|
||||
#define SOF_TKN_PCM_DMAC_CONFIG 353
|
||||
|
||||
@ -107,8 +113,7 @@
|
||||
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
|
||||
|
||||
/* SAI */
|
||||
#define SOF_TKN_IMX_SAI_FIRST_TOKEN 1000
|
||||
/* TODO: Add SAI tokens */
|
||||
#define SOF_TKN_IMX_SAI_MCLK_ID 1000
|
||||
|
||||
/* ESAI */
|
||||
#define SOF_TKN_IMX_ESAI_MCLK_ID 1100
|
||||
|
@ -836,7 +836,6 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int status;
|
||||
uint64_t size;
|
||||
u32 val = 0;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
@ -967,35 +966,19 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
|
||||
}
|
||||
|
||||
size = params_buffer_bytes(params);
|
||||
status = snd_pcm_lib_malloc_pages(substream, size);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
|
||||
acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
|
||||
/* Save for runtime private data */
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->order = get_order(size);
|
||||
|
||||
if (substream->dma_buffer.area) {
|
||||
acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
|
||||
/* Save for runtime private data */
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->order = get_order(size);
|
||||
/* Fill the page table entries in ACP SRAM */
|
||||
rtd->size = size;
|
||||
rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
rtd->direction = substream->stream;
|
||||
|
||||
/* Fill the page table entries in ACP SRAM */
|
||||
rtd->size = size;
|
||||
rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
rtd->direction = substream->stream;
|
||||
|
||||
config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
|
||||
status = 0;
|
||||
} else {
|
||||
status = -ENOMEM;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acp_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 acp_get_byte_count(struct audio_substream_data *rtd)
|
||||
@ -1142,18 +1125,18 @@ static int acp_dma_new(struct snd_soc_component *component,
|
||||
|
||||
switch (adata->asic_type) {
|
||||
case CHIP_STONEY:
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
parent,
|
||||
ST_MIN_BUFFER,
|
||||
ST_MAX_BUFFER);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
parent,
|
||||
ST_MIN_BUFFER,
|
||||
ST_MAX_BUFFER);
|
||||
break;
|
||||
default:
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
parent,
|
||||
MIN_BUFFER,
|
||||
MAX_BUFFER);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
parent,
|
||||
MIN_BUFFER,
|
||||
MAX_BUFFER);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@ -1219,9 +1202,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = {
|
||||
.name = DRV_NAME,
|
||||
.open = acp_dma_open,
|
||||
.close = acp_dma_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = acp_dma_hw_params,
|
||||
.hw_free = acp_dma_hw_free,
|
||||
.trigger = acp_dma_trigger,
|
||||
.pointer = acp_dma_pointer,
|
||||
.mmap = acp_dma_mmap,
|
||||
|
@ -2,5 +2,7 @@
|
||||
# Raven Ridge platform Support
|
||||
snd-pci-acp3x-objs := pci-acp3x.o
|
||||
snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o
|
||||
snd-acp3x-i2s-objs := acp3x-i2s.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-i2s.o
|
||||
|
358
sound/soc/amd/raven/acp3x-i2s.c
Normal file
358
sound/soc/amd/raven/acp3x-i2s.c
Normal file
@ -0,0 +1,358 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// AMD ALSA SoC PCM Driver
|
||||
//
|
||||
//Copyright 2016 Advanced Micro Devices, Inc.
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "acp3x.h"
|
||||
|
||||
#define DRV_NAME "acp3x-i2s"
|
||||
|
||||
static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct i2s_dev_data *adata;
|
||||
int mode;
|
||||
|
||||
adata = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
|
||||
switch (mode) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
adata->tdm_mode = TDM_DISABLE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
adata->tdm_mode = TDM_ENABLE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
|
||||
u32 tx_mask, u32 rx_mask, int slots, int slot_width)
|
||||
{
|
||||
struct i2s_dev_data *adata;
|
||||
u32 val, reg_val, frmt_reg, frm_len;
|
||||
u16 slot_len;
|
||||
|
||||
adata = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
/* These values are as per Hardware Spec */
|
||||
switch (slot_width) {
|
||||
case SLOT_WIDTH_8:
|
||||
slot_len = 8;
|
||||
break;
|
||||
case SLOT_WIDTH_16:
|
||||
slot_len = 16;
|
||||
break;
|
||||
case SLOT_WIDTH_24:
|
||||
slot_len = 24;
|
||||
break;
|
||||
case SLOT_WIDTH_32:
|
||||
slot_len = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Enable I2S/BT channels TDM, respective TX/RX frame lengths.*/
|
||||
|
||||
frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
|
||||
if (adata->substream_type == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (adata->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_ITER;
|
||||
frmt_reg = mmACP_BTTDM_TXFRMT;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_ITER;
|
||||
frmt_reg = mmACP_I2STDM_TXFRMT;
|
||||
}
|
||||
} else {
|
||||
switch (adata->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_IRER;
|
||||
frmt_reg = mmACP_BTTDM_RXFRMT;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_IRER;
|
||||
frmt_reg = mmACP_I2STDM_RXFRMT;
|
||||
}
|
||||
}
|
||||
val = rv_readl(adata->acp3x_base + reg_val);
|
||||
rv_writel(val | 0x2, adata->acp3x_base + reg_val);
|
||||
rv_writel(frm_len, adata->acp3x_base + frmt_reg);
|
||||
adata->tdm_fmt = frm_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct i2s_stream_instance *rtd;
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
u32 val;
|
||||
u32 reg_val;
|
||||
|
||||
prtd = substream->private_data;
|
||||
rtd = substream->runtime->private_data;
|
||||
card = prtd->card;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
if (pinfo) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
}
|
||||
|
||||
/* These values are as per Hardware Spec */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_U8:
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
rtd->xfer_resolution = 0x0;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
rtd->xfer_resolution = 0x02;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
rtd->xfer_resolution = 0x04;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
rtd->xfer_resolution = 0x05;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_ITER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_ITER;
|
||||
}
|
||||
} else {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_IRER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_IRER;
|
||||
}
|
||||
}
|
||||
val = rv_readl(rtd->acp3x_base + reg_val);
|
||||
val = val | (rtd->xfer_resolution << 3);
|
||||
rv_writel(val, rtd->acp3x_base + reg_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct i2s_stream_instance *rtd;
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
u32 ret, val, period_bytes, reg_val, ier_val, water_val;
|
||||
|
||||
prtd = substream->private_data;
|
||||
rtd = substream->runtime->private_data;
|
||||
card = prtd->card;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
if (pinfo) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
}
|
||||
period_bytes = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->period_size);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
rtd->bytescount = acp_get_byte_count(rtd,
|
||||
substream->stream);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
water_val =
|
||||
mmACP_BT_TX_INTR_WATERMARK_SIZE;
|
||||
reg_val = mmACP_BTTDM_ITER;
|
||||
ier_val = mmACP_BTTDM_IER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
water_val =
|
||||
mmACP_I2S_TX_INTR_WATERMARK_SIZE;
|
||||
reg_val = mmACP_I2STDM_ITER;
|
||||
ier_val = mmACP_I2STDM_IER;
|
||||
}
|
||||
} else {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
water_val =
|
||||
mmACP_BT_RX_INTR_WATERMARK_SIZE;
|
||||
reg_val = mmACP_BTTDM_IRER;
|
||||
ier_val = mmACP_BTTDM_IER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
water_val =
|
||||
mmACP_I2S_RX_INTR_WATERMARK_SIZE;
|
||||
reg_val = mmACP_I2STDM_IRER;
|
||||
ier_val = mmACP_I2STDM_IER;
|
||||
}
|
||||
}
|
||||
rv_writel(period_bytes, rtd->acp3x_base + water_val);
|
||||
val = rv_readl(rtd->acp3x_base + reg_val);
|
||||
val = val | BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + reg_val);
|
||||
rv_writel(1, rtd->acp3x_base + ier_val);
|
||||
ret = 0;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_ITER;
|
||||
ier_val = mmACP_BTTDM_IER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_ITER;
|
||||
ier_val = mmACP_I2STDM_IER;
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_IRER;
|
||||
ier_val = mmACP_BTTDM_IER;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_IRER;
|
||||
ier_val = mmACP_I2STDM_IER;
|
||||
}
|
||||
}
|
||||
val = rv_readl(rtd->acp3x_base + reg_val);
|
||||
val = val & ~BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + reg_val);
|
||||
rv_writel(0, rtd->acp3x_base + ier_val);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
|
||||
.hw_params = acp3x_i2s_hwparams,
|
||||
.trigger = acp3x_i2s_trigger,
|
||||
.set_fmt = acp3x_i2s_set_fmt,
|
||||
.set_tdm_slot = acp3x_i2s_set_tdm_slot,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp3x_dai_component = {
|
||||
.name = "acp3x-i2s",
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver acp3x_i2s_dai = {
|
||||
.playback = {
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
},
|
||||
.capture = {
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &acp3x_i2s_dai_ops,
|
||||
};
|
||||
|
||||
static int acp3x_dai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct i2s_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
|
||||
GFP_KERNEL);
|
||||
if (!adata)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!adata->acp3x_base)
|
||||
return -ENOMEM;
|
||||
|
||||
adata->i2s_irq = res->start;
|
||||
dev_set_drvdata(&pdev->dev, adata);
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&acp3x_dai_component, &acp3x_i2s_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dai_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* As we use devm_ memory alloc there is nothing TBD here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver acp3x_dai_driver = {
|
||||
.probe = acp3x_dai_probe,
|
||||
.remove = acp3x_dai_remove,
|
||||
.driver = {
|
||||
.name = "acp3x_i2s_playcap",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(acp3x_dai_driver);
|
||||
|
||||
MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
@ -9,7 +9,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
@ -18,28 +17,11 @@
|
||||
|
||||
#define DRV_NAME "acp3x-i2s-audio"
|
||||
|
||||
struct i2s_dev_data {
|
||||
bool tdm_mode;
|
||||
unsigned int i2s_irq;
|
||||
u32 tdm_fmt;
|
||||
void __iomem *acp3x_base;
|
||||
struct snd_pcm_substream *play_stream;
|
||||
struct snd_pcm_substream *capture_stream;
|
||||
};
|
||||
|
||||
struct i2s_stream_instance {
|
||||
u16 num_pages;
|
||||
u16 channels;
|
||||
u32 xfer_resolution;
|
||||
u64 bytescount;
|
||||
dma_addr_t dma_addr;
|
||||
void __iomem *acp3x_base;
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
@ -60,7 +42,8 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
@ -76,112 +59,13 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
|
||||
.periods_max = CAPTURE_MAX_NUM_PERIODS,
|
||||
};
|
||||
|
||||
static int acp3x_power_on(void __iomem *acp3x_base, bool on)
|
||||
{
|
||||
u16 val, mask;
|
||||
u32 timeout;
|
||||
|
||||
if (on == true) {
|
||||
val = 1;
|
||||
mask = ACP3x_POWER_ON;
|
||||
} else {
|
||||
val = 0;
|
||||
mask = ACP3x_POWER_OFF;
|
||||
}
|
||||
|
||||
rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL);
|
||||
timeout = 0;
|
||||
while (true) {
|
||||
val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
|
||||
if ((val & ACP3x_POWER_OFF_IN_PROGRESS) == mask)
|
||||
break;
|
||||
if (timeout > 100) {
|
||||
pr_err("ACP3x power state change failure\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
timeout++;
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_reset(void __iomem *acp3x_base)
|
||||
{
|
||||
u32 val, timeout;
|
||||
|
||||
rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (true) {
|
||||
val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
|
||||
if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) ||
|
||||
timeout > 100) {
|
||||
if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
|
||||
break;
|
||||
return -ENODEV;
|
||||
}
|
||||
timeout++;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (true) {
|
||||
val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
|
||||
if (!val || timeout > 100) {
|
||||
if (!val)
|
||||
break;
|
||||
return -ENODEV;
|
||||
}
|
||||
timeout++;
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_init(void __iomem *acp3x_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = acp3x_power_on(acp3x_base, true);
|
||||
if (ret) {
|
||||
pr_err("ACP3x power on failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* Reset */
|
||||
ret = acp3x_reset(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_deinit(void __iomem *acp3x_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Reset */
|
||||
ret = acp3x_reset(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* power off */
|
||||
ret = acp3x_power_on(acp3x_base, false);
|
||||
if (ret) {
|
||||
pr_err("ACP3x power off failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct i2s_dev_data *rv_i2s_data;
|
||||
u16 play_flag, cap_flag;
|
||||
u32 val;
|
||||
struct i2s_dev_data *rv_i2s_data = dev_id;
|
||||
|
||||
rv_i2s_data = dev_id;
|
||||
if (!rv_i2s_data)
|
||||
return IRQ_NONE;
|
||||
|
||||
@ -194,6 +78,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||
snd_pcm_period_elapsed(rv_i2s_data->play_stream);
|
||||
play_flag = 1;
|
||||
}
|
||||
if ((val & BIT(I2S_TX_THRESHOLD)) &&
|
||||
rv_i2s_data->i2ssp_play_stream) {
|
||||
rv_writel(BIT(I2S_TX_THRESHOLD),
|
||||
rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
|
||||
snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream);
|
||||
play_flag = 1;
|
||||
}
|
||||
|
||||
if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) {
|
||||
rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base +
|
||||
@ -201,6 +92,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||
snd_pcm_period_elapsed(rv_i2s_data->capture_stream);
|
||||
cap_flag = 1;
|
||||
}
|
||||
if ((val & BIT(I2S_RX_THRESHOLD)) &&
|
||||
rv_i2s_data->i2ssp_capture_stream) {
|
||||
rv_writel(BIT(I2S_RX_THRESHOLD),
|
||||
rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
|
||||
snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream);
|
||||
cap_flag = 1;
|
||||
}
|
||||
|
||||
if (play_flag | cap_flag)
|
||||
return IRQ_HANDLED;
|
||||
@ -211,15 +109,31 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||
static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
|
||||
{
|
||||
u16 page_idx;
|
||||
u32 low, high, val, acp_fifo_addr;
|
||||
dma_addr_t addr = rtd->dma_addr;
|
||||
u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
|
||||
u32 reg_ringbuf_size, reg_dma_size, reg_fifo_size;
|
||||
dma_addr_t addr;
|
||||
|
||||
/* 8 scratch registers used to map one 64 bit address */
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
val = 0;
|
||||
else
|
||||
val = rtd->num_pages * 8;
|
||||
addr = rtd->dma_addr;
|
||||
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
val = ACP_SRAM_BT_PB_PTE_OFFSET;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
val = ACP_SRAM_SP_PB_PTE_OFFSET;
|
||||
}
|
||||
} else {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
val = ACP_SRAM_BT_CP_PTE_OFFSET;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
val = ACP_SRAM_SP_CP_PTE_OFFSET;
|
||||
}
|
||||
}
|
||||
/* Group Enable */
|
||||
rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base +
|
||||
mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
|
||||
@ -241,48 +155,77 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
|
||||
}
|
||||
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* Config ringbuffer */
|
||||
rv_writel(MEM_WINDOW_START, rtd->acp3x_base +
|
||||
mmACP_BT_TX_RINGBUFADDR);
|
||||
rv_writel(MAX_BUFFER, rtd->acp3x_base +
|
||||
mmACP_BT_TX_RINGBUFSIZE);
|
||||
rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_TX_DMA_SIZE);
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_ringbuf_size = mmACP_BT_TX_RINGBUFSIZE;
|
||||
reg_dma_size = mmACP_BT_TX_DMA_SIZE;
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
|
||||
BT_PB_FIFO_ADDR_OFFSET;
|
||||
reg_fifo_addr = mmACP_BT_TX_FIFOADDR;
|
||||
reg_fifo_size = mmACP_BT_TX_FIFOSIZE;
|
||||
rv_writel(I2S_BT_TX_MEM_WINDOW_START,
|
||||
rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR);
|
||||
break;
|
||||
|
||||
/* Config audio fifo */
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET + (rtd->num_pages * 8)
|
||||
+ PLAYBACK_FIFO_ADDR_OFFSET;
|
||||
rv_writel(acp_fifo_addr, rtd->acp3x_base +
|
||||
mmACP_BT_TX_FIFOADDR);
|
||||
rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_TX_FIFOSIZE);
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_ringbuf_size = mmACP_I2S_TX_RINGBUFSIZE;
|
||||
reg_dma_size = mmACP_I2S_TX_DMA_SIZE;
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
|
||||
SP_PB_FIFO_ADDR_OFFSET;
|
||||
reg_fifo_addr = mmACP_I2S_TX_FIFOADDR;
|
||||
reg_fifo_size = mmACP_I2S_TX_FIFOSIZE;
|
||||
rv_writel(I2S_SP_TX_MEM_WINDOW_START,
|
||||
rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR);
|
||||
}
|
||||
} else {
|
||||
/* Config ringbuffer */
|
||||
rv_writel(MEM_WINDOW_START + MAX_BUFFER, rtd->acp3x_base +
|
||||
mmACP_BT_RX_RINGBUFADDR);
|
||||
rv_writel(MAX_BUFFER, rtd->acp3x_base +
|
||||
mmACP_BT_RX_RINGBUFSIZE);
|
||||
rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_RX_DMA_SIZE);
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_ringbuf_size = mmACP_BT_RX_RINGBUFSIZE;
|
||||
reg_dma_size = mmACP_BT_RX_DMA_SIZE;
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
|
||||
BT_CAPT_FIFO_ADDR_OFFSET;
|
||||
reg_fifo_addr = mmACP_BT_RX_FIFOADDR;
|
||||
reg_fifo_size = mmACP_BT_RX_FIFOSIZE;
|
||||
rv_writel(I2S_BT_RX_MEM_WINDOW_START,
|
||||
rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR);
|
||||
break;
|
||||
|
||||
/* Config audio fifo */
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
|
||||
(rtd->num_pages * 8) + CAPTURE_FIFO_ADDR_OFFSET;
|
||||
rv_writel(acp_fifo_addr, rtd->acp3x_base +
|
||||
mmACP_BT_RX_FIFOADDR);
|
||||
rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_RX_FIFOSIZE);
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_ringbuf_size = mmACP_I2S_RX_RINGBUFSIZE;
|
||||
reg_dma_size = mmACP_I2S_RX_DMA_SIZE;
|
||||
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
|
||||
SP_CAPT_FIFO_ADDR_OFFSET;
|
||||
reg_fifo_addr = mmACP_I2S_RX_FIFOADDR;
|
||||
reg_fifo_size = mmACP_I2S_RX_FIFOSIZE;
|
||||
rv_writel(I2S_SP_RX_MEM_WINDOW_START,
|
||||
rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable watermark/period interrupt to host */
|
||||
rv_writel(BIT(BT_TX_THRESHOLD) | BIT(BT_RX_THRESHOLD),
|
||||
rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
|
||||
rv_writel(MAX_BUFFER, rtd->acp3x_base + reg_ringbuf_size);
|
||||
rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size);
|
||||
rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr);
|
||||
rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size);
|
||||
rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
|
||||
| BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD),
|
||||
rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static int acp3x_dma_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance),
|
||||
GFP_KERNEL);
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct i2s_dev_data *adata;
|
||||
struct i2s_stream_instance *i2s_data;
|
||||
int ret;
|
||||
|
||||
runtime = substream->runtime;
|
||||
prtd = substream->private_data;
|
||||
component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
|
||||
adata = dev_get_drvdata(component->dev);
|
||||
i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
|
||||
if (!i2s_data)
|
||||
return -EINVAL;
|
||||
|
||||
@ -299,74 +242,77 @@ static int acp3x_dma_open(struct snd_soc_component *component,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!adata->play_stream && !adata->capture_stream)
|
||||
if (!adata->play_stream && !adata->capture_stream &&
|
||||
adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
|
||||
rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adata->play_stream = substream;
|
||||
else
|
||||
adata->i2ssp_play_stream = substream;
|
||||
} else {
|
||||
adata->capture_stream = substream;
|
||||
adata->i2ssp_capture_stream = substream;
|
||||
}
|
||||
|
||||
i2s_data->acp3x_base = adata->acp3x_base;
|
||||
runtime->private_data = i2s_data;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction)
|
||||
{
|
||||
u64 byte_count;
|
||||
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_TX_LINEARPOSITIONCNTR_LOW);
|
||||
} else {
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_RX_LINEARPOSITIONCNTR_LOW);
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
static int acp3x_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int status;
|
||||
struct i2s_stream_instance *rtd;
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
u64 size;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct i2s_stream_instance *rtd = runtime->private_data;
|
||||
|
||||
prtd = substream->private_data;
|
||||
card = prtd->card;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
rtd = substream->runtime->private_data;
|
||||
if (!rtd)
|
||||
return -EINVAL;
|
||||
|
||||
size = params_buffer_bytes(params);
|
||||
status = snd_pcm_lib_malloc_pages(substream, size);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (pinfo)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
else
|
||||
pr_err("pinfo failed\n");
|
||||
|
||||
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
|
||||
if (substream->dma_buffer.area) {
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
config_acp3x_dma(rtd, substream->stream);
|
||||
status = 0;
|
||||
} else {
|
||||
status = -ENOMEM;
|
||||
}
|
||||
return status;
|
||||
size = params_buffer_bytes(params);
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
config_acp3x_dma(rtd, substream->stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
u32 pos = 0;
|
||||
u32 buffersize = 0;
|
||||
u64 bytescount = 0;
|
||||
struct i2s_stream_instance *rtd =
|
||||
substream->runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
struct i2s_stream_instance *rtd;
|
||||
u32 pos;
|
||||
u32 buffersize;
|
||||
u64 bytescount;
|
||||
|
||||
prtd = substream->private_data;
|
||||
card = prtd->card;
|
||||
rtd = substream->runtime->private_data;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
if (pinfo) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
}
|
||||
|
||||
buffersize = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->buffer_size);
|
||||
@ -381,17 +327,11 @@ static int acp3x_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct device *parent = component->dev->parent;
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
|
||||
parent, MIN_BUFFER, MAX_BUFFER);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
|
||||
parent, MIN_BUFFER, MAX_BUFFER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int acp3x_dma_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
@ -402,206 +342,35 @@ static int acp3x_dma_mmap(struct snd_soc_component *component,
|
||||
static int acp3x_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct i2s_stream_instance *rtd = substream->runtime->private_data;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct i2s_dev_data *adata;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd = substream->private_data;
|
||||
component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
|
||||
adata = dev_get_drvdata(component->dev);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adata->play_stream = NULL;
|
||||
else
|
||||
adata->i2ssp_play_stream = NULL;
|
||||
} else {
|
||||
adata->capture_stream = NULL;
|
||||
adata->i2ssp_capture_stream = NULL;
|
||||
}
|
||||
|
||||
/* Disable ACP irq, when the current stream is being closed and
|
||||
* another stream is also not active.
|
||||
*/
|
||||
if (!adata->play_stream && !adata->capture_stream)
|
||||
if (!adata->play_stream && !adata->capture_stream &&
|
||||
!adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
|
||||
rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
kfree(rtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
{
|
||||
|
||||
struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
adata->tdm_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
adata->tdm_mode = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
|
||||
u32 rx_mask, int slots, int slot_width)
|
||||
{
|
||||
u32 val = 0;
|
||||
u16 slot_len;
|
||||
|
||||
struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
switch (slot_width) {
|
||||
case SLOT_WIDTH_8:
|
||||
slot_len = 8;
|
||||
break;
|
||||
case SLOT_WIDTH_16:
|
||||
slot_len = 16;
|
||||
break;
|
||||
case SLOT_WIDTH_24:
|
||||
slot_len = 24;
|
||||
break;
|
||||
case SLOT_WIDTH_32:
|
||||
slot_len = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER);
|
||||
rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_ITER);
|
||||
val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER);
|
||||
rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_IRER);
|
||||
|
||||
val = (FRM_LEN | (slots << 15) | (slot_len << 18));
|
||||
rv_writel(val, adata->acp3x_base + mmACP_BTTDM_TXFRMT);
|
||||
rv_writel(val, adata->acp3x_base + mmACP_BTTDM_RXFRMT);
|
||||
|
||||
adata->tdm_fmt = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dai_i2s_hwparams(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
u32 val = 0;
|
||||
struct i2s_stream_instance *rtd = substream->runtime->private_data;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_U8:
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
rtd->xfer_resolution = 0x0;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
rtd->xfer_resolution = 0x02;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
rtd->xfer_resolution = 0x04;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
rtd->xfer_resolution = 0x05;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
val = val | (rtd->xfer_resolution << 3);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
else
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2s_stream_instance *rtd = substream->runtime->private_data;
|
||||
u32 val, period_bytes;
|
||||
|
||||
period_bytes = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->period_size);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
rtd->bytescount = acp_get_byte_count(rtd, substream->stream);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
rv_writel(period_bytes, rtd->acp3x_base +
|
||||
mmACP_BT_TX_INTR_WATERMARK_SIZE);
|
||||
val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
val = val | BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
} else {
|
||||
rv_writel(period_bytes, rtd->acp3x_base +
|
||||
mmACP_BT_RX_INTR_WATERMARK_SIZE);
|
||||
val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
val = val | BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
}
|
||||
rv_writel(1, rtd->acp3x_base + mmACP_BTTDM_IER);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
val = val & ~BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
} else {
|
||||
val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
val = val & ~BIT(0);
|
||||
rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
}
|
||||
rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
|
||||
.hw_params = acp3x_dai_i2s_hwparams,
|
||||
.trigger = acp3x_dai_i2s_trigger,
|
||||
.set_fmt = acp3x_dai_i2s_set_fmt,
|
||||
.set_tdm_slot = acp3x_dai_set_tdm_slot,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver acp3x_i2s_dai_driver = {
|
||||
.playback = {
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
},
|
||||
.capture = {
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &acp3x_dai_i2s_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp3x_i2s_component = {
|
||||
.name = DRV_NAME,
|
||||
.open = acp3x_dma_open,
|
||||
.close = acp3x_dma_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = acp3x_dma_hw_params,
|
||||
.hw_free = acp3x_dma_hw_free,
|
||||
.pointer = acp3x_dma_pointer,
|
||||
.mmap = acp3x_dma_mmap,
|
||||
.pcm_construct = acp3x_dma_new,
|
||||
@ -609,10 +378,10 @@ static const struct snd_soc_component_driver acp3x_i2s_component = {
|
||||
|
||||
static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int status;
|
||||
struct resource *res;
|
||||
struct i2s_dev_data *adata;
|
||||
unsigned int irqflags;
|
||||
int status;
|
||||
|
||||
if (!pdev->dev.platform_data) {
|
||||
dev_err(&pdev->dev, "platform_data not retrieved\n");
|
||||
@ -622,7 +391,7 @@ static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
|
||||
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -632,6 +401,8 @@ static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
|
||||
adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!adata->acp3x_base)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
@ -640,97 +411,81 @@ static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
adata->i2s_irq = res->start;
|
||||
adata->play_stream = NULL;
|
||||
adata->capture_stream = NULL;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, adata);
|
||||
/* Initialize ACP */
|
||||
status = acp3x_init(adata->acp3x_base);
|
||||
if (status)
|
||||
return -ENODEV;
|
||||
status = devm_snd_soc_register_component(&pdev->dev,
|
||||
&acp3x_i2s_component,
|
||||
&acp3x_i2s_dai_driver, 1);
|
||||
NULL, 0);
|
||||
if (status) {
|
||||
dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
|
||||
goto dev_err;
|
||||
dev_err(&pdev->dev, "Fail to register acp i2s component\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
|
||||
irqflags, "ACP3x_I2S_IRQ", adata);
|
||||
if (status) {
|
||||
dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n");
|
||||
goto dev_err;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 10000);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
return 0;
|
||||
dev_err:
|
||||
status = acp3x_deinit(adata->acp3x_base);
|
||||
if (status)
|
||||
dev_err(&pdev->dev, "ACP de-init failed\n");
|
||||
else
|
||||
dev_info(&pdev->dev, "ACP de-initialized\n");
|
||||
/*ignore device status and return driver probe error*/
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int acp3x_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
ret = acp3x_deinit(adata->acp3x_base);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "ACP de-init failed\n");
|
||||
else
|
||||
dev_info(&pdev->dev, "ACP de-initialized\n");
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_resume(struct device *dev)
|
||||
{
|
||||
int status;
|
||||
u32 val;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(dev);
|
||||
struct i2s_dev_data *adata;
|
||||
u32 val, reg_val, frmt_val;
|
||||
|
||||
status = acp3x_init(adata->acp3x_base);
|
||||
if (status)
|
||||
return -ENODEV;
|
||||
reg_val = 0;
|
||||
frmt_val = 0;
|
||||
adata = dev_get_drvdata(dev);
|
||||
|
||||
if (adata->play_stream && adata->play_stream->runtime) {
|
||||
struct i2s_stream_instance *rtd =
|
||||
adata->play_stream->runtime->private_data;
|
||||
config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
rv_writel((rtd->xfer_resolution << 3),
|
||||
rtd->acp3x_base + mmACP_BTTDM_ITER);
|
||||
if (adata->tdm_mode == true) {
|
||||
rv_writel(adata->tdm_fmt, adata->acp3x_base +
|
||||
mmACP_BTTDM_TXFRMT);
|
||||
val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER);
|
||||
rv_writel((val | 0x2), adata->acp3x_base +
|
||||
mmACP_BTTDM_ITER);
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_ITER;
|
||||
frmt_val = mmACP_BTTDM_TXFRMT;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_ITER;
|
||||
frmt_val = mmACP_I2STDM_TXFRMT;
|
||||
}
|
||||
rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val);
|
||||
}
|
||||
|
||||
if (adata->capture_stream && adata->capture_stream->runtime) {
|
||||
struct i2s_stream_instance *rtd =
|
||||
adata->capture_stream->runtime->private_data;
|
||||
config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
|
||||
rv_writel((rtd->xfer_resolution << 3),
|
||||
rtd->acp3x_base + mmACP_BTTDM_IRER);
|
||||
if (adata->tdm_mode == true) {
|
||||
rv_writel(adata->tdm_fmt, adata->acp3x_base +
|
||||
mmACP_BTTDM_RXFRMT);
|
||||
val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER);
|
||||
rv_writel((val | 0x2), adata->acp3x_base +
|
||||
mmACP_BTTDM_IRER);
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
reg_val = mmACP_BTTDM_IRER;
|
||||
frmt_val = mmACP_BTTDM_RXFRMT;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
reg_val = mmACP_I2STDM_IRER;
|
||||
frmt_val = mmACP_I2STDM_RXFRMT;
|
||||
}
|
||||
rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val);
|
||||
}
|
||||
if (adata->tdm_mode == TDM_ENABLE) {
|
||||
rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val);
|
||||
val = rv_readl(adata->acp3x_base + reg_val);
|
||||
rv_writel(val | 0x2, adata->acp3x_base + reg_val);
|
||||
}
|
||||
|
||||
rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
return 0;
|
||||
}
|
||||
@ -738,14 +493,9 @@ static int acp3x_resume(struct device *dev)
|
||||
|
||||
static int acp3x_pcm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int status;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(dev);
|
||||
struct i2s_dev_data *adata;
|
||||
|
||||
status = acp3x_deinit(adata->acp3x_base);
|
||||
if (status)
|
||||
dev_err(dev, "ACP de-init failed\n");
|
||||
else
|
||||
dev_info(dev, "ACP de-initialized\n");
|
||||
adata = dev_get_drvdata(dev);
|
||||
|
||||
rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
|
||||
@ -754,12 +504,10 @@ static int acp3x_pcm_runtime_suspend(struct device *dev)
|
||||
|
||||
static int acp3x_pcm_runtime_resume(struct device *dev)
|
||||
{
|
||||
int status;
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(dev);
|
||||
struct i2s_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
|
||||
status = acp3x_init(adata->acp3x_base);
|
||||
if (status)
|
||||
return -ENODEV;
|
||||
rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
return 0;
|
||||
}
|
||||
@ -774,13 +522,14 @@ static struct platform_driver acp3x_dma_driver = {
|
||||
.probe = acp3x_audio_probe,
|
||||
.remove = acp3x_audio_remove,
|
||||
.driver = {
|
||||
.name = "acp3x_rv_i2s",
|
||||
.name = "acp3x_rv_i2s_dma",
|
||||
.pm = &acp3x_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(acp3x_dma_driver);
|
||||
|
||||
MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
|
||||
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
|
||||
|
@ -6,14 +6,28 @@
|
||||
*/
|
||||
|
||||
#include "chip_offset_byte.h"
|
||||
#include <sound/pcm.h>
|
||||
#define I2S_SP_INSTANCE 0x01
|
||||
#define I2S_BT_INSTANCE 0x02
|
||||
|
||||
#define TDM_ENABLE 1
|
||||
#define TDM_DISABLE 0
|
||||
|
||||
#define ACP3x_DEVS 4
|
||||
#define ACP3x_PHY_BASE_ADDRESS 0x1240000
|
||||
#define ACP3x_I2S_MODE 0
|
||||
#define ACP3x_REG_START 0x1240000
|
||||
#define ACP3x_REG_END 0x1250200
|
||||
#define ACP3x_I2STDM_REG_START 0x1242400
|
||||
#define ACP3x_I2STDM_REG_END 0x1242410
|
||||
#define ACP3x_BT_TDM_REG_START 0x1242800
|
||||
#define ACP3x_BT_TDM_REG_END 0x1242810
|
||||
#define I2S_MODE 0x04
|
||||
#define I2S_RX_THRESHOLD 27
|
||||
#define I2S_TX_THRESHOLD 28
|
||||
#define BT_TX_THRESHOLD 26
|
||||
#define BT_RX_THRESHOLD 25
|
||||
#define ACP_ERR_INTR_MASK 29
|
||||
#define ACP3x_POWER_ON 0x00
|
||||
#define ACP3x_POWER_ON_IN_PROGRESS 0x01
|
||||
#define ACP3x_POWER_OFF 0x02
|
||||
@ -21,19 +35,28 @@
|
||||
#define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001
|
||||
|
||||
#define ACP_SRAM_PTE_OFFSET 0x02050000
|
||||
#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
|
||||
#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
|
||||
#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
|
||||
#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
|
||||
#define PAGE_SIZE_4K_ENABLE 0x2
|
||||
#define MEM_WINDOW_START 0x4000000
|
||||
#define PLAYBACK_FIFO_ADDR_OFFSET 0x400
|
||||
#define CAPTURE_FIFO_ADDR_OFFSET 0x500
|
||||
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
|
||||
#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
|
||||
#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
|
||||
#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
|
||||
|
||||
#define SP_PB_FIFO_ADDR_OFFSET 0x500
|
||||
#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
|
||||
#define BT_PB_FIFO_ADDR_OFFSET 0x900
|
||||
#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
|
||||
#define PLAYBACK_MIN_NUM_PERIODS 2
|
||||
#define PLAYBACK_MAX_NUM_PERIODS 8
|
||||
#define PLAYBACK_MAX_PERIOD_SIZE 16384
|
||||
#define PLAYBACK_MIN_PERIOD_SIZE 4096
|
||||
#define PLAYBACK_MAX_PERIOD_SIZE 8192
|
||||
#define PLAYBACK_MIN_PERIOD_SIZE 1024
|
||||
#define CAPTURE_MIN_NUM_PERIODS 2
|
||||
#define CAPTURE_MAX_NUM_PERIODS 8
|
||||
#define CAPTURE_MAX_PERIOD_SIZE 16384
|
||||
#define CAPTURE_MIN_PERIOD_SIZE 4096
|
||||
#define CAPTURE_MAX_PERIOD_SIZE 8192
|
||||
#define CAPTURE_MIN_PERIOD_SIZE 1024
|
||||
|
||||
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
|
||||
#define MIN_BUFFER MAX_BUFFER
|
||||
@ -45,7 +68,45 @@
|
||||
#define SLOT_WIDTH_16 0x10
|
||||
#define SLOT_WIDTH_24 0x18
|
||||
#define SLOT_WIDTH_32 0x20
|
||||
#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
|
||||
#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
|
||||
#define ACP_PGFSM_STATUS_MASK 0x03
|
||||
#define ACP_POWERED_ON 0x00
|
||||
#define ACP_POWER_ON_IN_PROGRESS 0x01
|
||||
#define ACP_POWERED_OFF 0x02
|
||||
#define ACP_POWER_OFF_IN_PROGRESS 0x03
|
||||
|
||||
struct acp3x_platform_info {
|
||||
u16 play_i2s_instance;
|
||||
u16 cap_i2s_instance;
|
||||
u16 capture_channel;
|
||||
};
|
||||
|
||||
struct i2s_dev_data {
|
||||
bool tdm_mode;
|
||||
unsigned int i2s_irq;
|
||||
u16 i2s_instance;
|
||||
u32 tdm_fmt;
|
||||
u32 substream_type;
|
||||
void __iomem *acp3x_base;
|
||||
struct snd_pcm_substream *play_stream;
|
||||
struct snd_pcm_substream *capture_stream;
|
||||
struct snd_pcm_substream *i2ssp_play_stream;
|
||||
struct snd_pcm_substream *i2ssp_capture_stream;
|
||||
};
|
||||
|
||||
struct i2s_stream_instance {
|
||||
u16 num_pages;
|
||||
u16 i2s_instance;
|
||||
u16 capture_channel;
|
||||
u16 direction;
|
||||
u16 channels;
|
||||
u32 xfer_resolution;
|
||||
u32 val;
|
||||
dma_addr_t dma_addr;
|
||||
u64 bytescount;
|
||||
void __iomem *acp3x_base;
|
||||
};
|
||||
|
||||
static inline u32 rv_readl(void __iomem *base_addr)
|
||||
{
|
||||
@ -56,3 +117,43 @@ static inline void rv_writel(u32 val, void __iomem *base_addr)
|
||||
{
|
||||
writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS);
|
||||
}
|
||||
|
||||
static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
|
||||
int direction)
|
||||
{
|
||||
u64 byte_count;
|
||||
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_TX_LINEARPOSITIONCNTR_LOW);
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_BT_RX_LINEARPOSITIONCNTR_LOW);
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
byte_count = rv_readl(rtd->acp3x_base +
|
||||
mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count |= rv_readl(rtd->acp3x_base +
|
||||
mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
|
||||
}
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "acp3x.h"
|
||||
|
||||
@ -16,17 +18,120 @@ struct acp3x_dev_data {
|
||||
void __iomem *acp3x_base;
|
||||
bool acp3x_audio_mode;
|
||||
struct resource *res;
|
||||
struct platform_device *pdev;
|
||||
struct platform_device *pdev[ACP3x_DEVS];
|
||||
};
|
||||
|
||||
static int acp3x_power_on(void __iomem *acp3x_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
|
||||
|
||||
if (val == 0)
|
||||
return val;
|
||||
|
||||
if (!((val & ACP_PGFSM_STATUS_MASK) ==
|
||||
ACP_POWER_ON_IN_PROGRESS))
|
||||
rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
|
||||
acp3x_base + mmACP_PGFSM_CONTROL);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
|
||||
if (!val)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int acp3x_power_off(void __iomem *acp3x_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
|
||||
acp3x_base + mmACP_PGFSM_CONTROL);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
|
||||
if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int acp3x_reset(void __iomem *acp3x_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
|
||||
if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
|
||||
if (!val)
|
||||
return 0;
|
||||
cpu_relax();
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int acp3x_init(void __iomem *acp3x_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = acp3x_power_on(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x power on failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* Reset */
|
||||
ret = acp3x_reset(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_deinit(void __iomem *acp3x_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Reset */
|
||||
ret = acp3x_reset(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* power off */
|
||||
ret = acp3x_power_off(acp3x_base);
|
||||
if (ret) {
|
||||
pr_err("ACP3x power off failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_acp3x_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int ret;
|
||||
u32 addr, val;
|
||||
struct acp3x_dev_data *adata;
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device_info pdevinfo[ACP3x_DEVS];
|
||||
unsigned int irqflags;
|
||||
int ret, i;
|
||||
u32 addr, val;
|
||||
|
||||
if (pci_enable_device(pci)) {
|
||||
dev_err(&pci->dev, "pci_enable_device failed\n");
|
||||
@ -56,23 +161,27 @@ static int snd_acp3x_probe(struct pci_dev *pci,
|
||||
irqflags = 0;
|
||||
|
||||
addr = pci_resource_start(pci, 0);
|
||||
adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0));
|
||||
adata->acp3x_base = devm_ioremap(&pci->dev, addr,
|
||||
pci_resource_len(pci, 0));
|
||||
if (!adata->acp3x_base) {
|
||||
ret = -ENOMEM;
|
||||
goto release_regions;
|
||||
goto disable_msi;
|
||||
}
|
||||
pci_set_master(pci);
|
||||
pci_set_drvdata(pci, adata);
|
||||
ret = acp3x_init(adata->acp3x_base);
|
||||
if (ret)
|
||||
goto disable_msi;
|
||||
|
||||
val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
|
||||
switch (val) {
|
||||
case I2S_MODE:
|
||||
adata->res = devm_kzalloc(&pci->dev,
|
||||
sizeof(struct resource) * 2,
|
||||
sizeof(struct resource) * 4,
|
||||
GFP_KERNEL);
|
||||
if (!adata->res) {
|
||||
ret = -ENOMEM;
|
||||
goto unmap_mmio;
|
||||
goto de_init;
|
||||
}
|
||||
|
||||
adata->res[0].name = "acp3x_i2s_iomem";
|
||||
@ -80,40 +189,82 @@ static int snd_acp3x_probe(struct pci_dev *pci,
|
||||
adata->res[0].start = addr;
|
||||
adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
|
||||
|
||||
adata->res[1].name = "acp3x_i2s_irq";
|
||||
adata->res[1].flags = IORESOURCE_IRQ;
|
||||
adata->res[1].start = pci->irq;
|
||||
adata->res[1].end = pci->irq;
|
||||
adata->res[1].name = "acp3x_i2s_sp";
|
||||
adata->res[1].flags = IORESOURCE_MEM;
|
||||
adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
|
||||
adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
|
||||
|
||||
adata->res[2].name = "acp3x_i2s_bt";
|
||||
adata->res[2].flags = IORESOURCE_MEM;
|
||||
adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
|
||||
adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
|
||||
|
||||
adata->res[3].name = "acp3x_i2s_irq";
|
||||
adata->res[3].flags = IORESOURCE_IRQ;
|
||||
adata->res[3].start = pci->irq;
|
||||
adata->res[3].end = adata->res[3].start;
|
||||
|
||||
adata->acp3x_audio_mode = ACP3x_I2S_MODE;
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
pdevinfo.name = "acp3x_rv_i2s";
|
||||
pdevinfo.id = 0;
|
||||
pdevinfo.parent = &pci->dev;
|
||||
pdevinfo.num_res = 2;
|
||||
pdevinfo.res = adata->res;
|
||||
pdevinfo.data = &irqflags;
|
||||
pdevinfo.size_data = sizeof(irqflags);
|
||||
pdevinfo[0].name = "acp3x_rv_i2s_dma";
|
||||
pdevinfo[0].id = 0;
|
||||
pdevinfo[0].parent = &pci->dev;
|
||||
pdevinfo[0].num_res = 4;
|
||||
pdevinfo[0].res = &adata->res[0];
|
||||
pdevinfo[0].data = &irqflags;
|
||||
pdevinfo[0].size_data = sizeof(irqflags);
|
||||
|
||||
adata->pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(adata->pdev)) {
|
||||
dev_err(&pci->dev, "cannot register %s device\n",
|
||||
pdevinfo.name);
|
||||
ret = PTR_ERR(adata->pdev);
|
||||
goto unmap_mmio;
|
||||
pdevinfo[1].name = "acp3x_i2s_playcap";
|
||||
pdevinfo[1].id = 0;
|
||||
pdevinfo[1].parent = &pci->dev;
|
||||
pdevinfo[1].num_res = 1;
|
||||
pdevinfo[1].res = &adata->res[1];
|
||||
|
||||
pdevinfo[2].name = "acp3x_i2s_playcap";
|
||||
pdevinfo[2].id = 1;
|
||||
pdevinfo[2].parent = &pci->dev;
|
||||
pdevinfo[2].num_res = 1;
|
||||
pdevinfo[2].res = &adata->res[1];
|
||||
|
||||
pdevinfo[3].name = "acp3x_i2s_playcap";
|
||||
pdevinfo[3].id = 2;
|
||||
pdevinfo[3].parent = &pci->dev;
|
||||
pdevinfo[3].num_res = 1;
|
||||
pdevinfo[3].res = &adata->res[2];
|
||||
for (i = 0; i < ACP3x_DEVS; i++) {
|
||||
adata->pdev[i] =
|
||||
platform_device_register_full(&pdevinfo[i]);
|
||||
if (IS_ERR(adata->pdev[i])) {
|
||||
dev_err(&pci->dev, "cannot register %s device\n",
|
||||
pdevinfo[i].name);
|
||||
ret = PTR_ERR(adata->pdev[i]);
|
||||
goto unregister_devs;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
|
||||
ret = -ENODEV;
|
||||
goto unmap_mmio;
|
||||
goto disable_msi;
|
||||
}
|
||||
pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
|
||||
pm_runtime_use_autosuspend(&pci->dev);
|
||||
pm_runtime_set_active(&pci->dev);
|
||||
pm_runtime_put_noidle(&pci->dev);
|
||||
pm_runtime_enable(&pci->dev);
|
||||
pm_runtime_allow(&pci->dev);
|
||||
return 0;
|
||||
|
||||
unmap_mmio:
|
||||
unregister_devs:
|
||||
if (val == I2S_MODE)
|
||||
for (i = 0; i < ACP3x_DEVS; i++)
|
||||
platform_device_unregister(adata->pdev[i]);
|
||||
de_init:
|
||||
if (acp3x_deinit(adata->acp3x_base))
|
||||
dev_err(&pci->dev, "ACP de-init failed\n");
|
||||
disable_msi:
|
||||
pci_disable_msi(pci);
|
||||
iounmap(adata->acp3x_base);
|
||||
release_regions:
|
||||
pci_release_regions(pci);
|
||||
disable_pci:
|
||||
@ -122,13 +273,56 @@ disable_pci:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_acp3x_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct acp3x_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = acp3x_deinit(adata->acp3x_base);
|
||||
if (ret)
|
||||
dev_err(dev, "ACP de-init failed\n");
|
||||
else
|
||||
dev_dbg(dev, "ACP de-initialized\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_acp3x_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct acp3x_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = acp3x_init(adata->acp3x_base);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP init failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops acp3x_pm = {
|
||||
.runtime_suspend = snd_acp3x_suspend,
|
||||
.runtime_resume = snd_acp3x_resume,
|
||||
.resume = snd_acp3x_resume,
|
||||
};
|
||||
|
||||
static void snd_acp3x_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct acp3x_dev_data *adata = pci_get_drvdata(pci);
|
||||
|
||||
platform_device_unregister(adata->pdev);
|
||||
iounmap(adata->acp3x_base);
|
||||
struct acp3x_dev_data *adata;
|
||||
int i, ret;
|
||||
|
||||
adata = pci_get_drvdata(pci);
|
||||
if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
|
||||
for (i = 0; i < ACP3x_DEVS; i++)
|
||||
platform_device_unregister(adata->pdev[i]);
|
||||
}
|
||||
ret = acp3x_deinit(adata->acp3x_base);
|
||||
if (ret)
|
||||
dev_err(&pci->dev, "ACP de-init failed\n");
|
||||
pm_runtime_disable(&pci->dev);
|
||||
pm_runtime_get_noresume(&pci->dev);
|
||||
pci_disable_msi(pci);
|
||||
pci_release_regions(pci);
|
||||
pci_disable_device(pci);
|
||||
@ -147,10 +341,14 @@ static struct pci_driver acp3x_driver = {
|
||||
.id_table = snd_acp3x_ids,
|
||||
.probe = snd_acp3x_probe,
|
||||
.remove = snd_acp3x_remove,
|
||||
.driver = {
|
||||
.pm = &acp3x_pm,
|
||||
}
|
||||
};
|
||||
|
||||
module_pci_driver(acp3x_driver);
|
||||
|
||||
MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
|
||||
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP3x PCI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -19,6 +19,8 @@ config SND_ATMEL_SOC_DMA
|
||||
|
||||
config SND_ATMEL_SOC_SSC
|
||||
tristate
|
||||
select SND_ATMEL_SOC_DMA
|
||||
select SND_ATMEL_SOC_PDC
|
||||
|
||||
config SND_ATMEL_SOC_SSC_PDC
|
||||
tristate "SoC PCM DAI support for AT91 SSC controller using PDC"
|
||||
|
@ -379,7 +379,6 @@ static int atmel_pcm_close(struct snd_soc_component *component,
|
||||
static const struct snd_soc_component_driver atmel_soc_platform = {
|
||||
.open = atmel_pcm_open,
|
||||
.close = atmel_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = atmel_pcm_hw_params,
|
||||
.hw_free = atmel_pcm_hw_free,
|
||||
.prepare = atmel_pcm_prepare,
|
||||
|
@ -760,12 +760,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
|
||||
static int atmel_ssc_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
|
||||
struct platform_device *pdev = to_platform_device(component->dev);
|
||||
|
||||
if (!cpu_dai->active)
|
||||
if (!component->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[pdev->id];
|
||||
@ -787,15 +787,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
|
||||
static int atmel_ssc_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
|
||||
struct platform_device *pdev = to_platform_device(component->dev);
|
||||
u32 cr;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
if (!component->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[pdev->id];
|
||||
@ -839,8 +837,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver atmel_ssc_dai = {
|
||||
.suspend = atmel_ssc_suspend,
|
||||
.resume = atmel_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
@ -860,6 +856,8 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
|
||||
|
||||
static const struct snd_soc_component_driver atmel_ssc_component = {
|
||||
.name = "atmel-ssc",
|
||||
.suspend = atmel_ssc_suspend,
|
||||
.resume = atmel_ssc_resume,
|
||||
};
|
||||
|
||||
static int asoc_ssc_init(struct device *dev)
|
||||
|
@ -206,7 +206,6 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
|
||||
|
||||
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
|
||||
.name = "alchemy-ac97c",
|
||||
.bus_control = true,
|
||||
.probe = au1xac97c_dai_probe,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
|
@ -197,10 +197,6 @@ static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct au1xpsc_audio_dmadata *pcd;
|
||||
int stype, ret;
|
||||
|
||||
ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
stype = substream->stream;
|
||||
pcd = to_dmadata(substream, component);
|
||||
|
||||
@ -232,13 +228,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
@ -315,7 +304,7 @@ static int au1xpsc_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
|
||||
|
||||
return 0;
|
||||
@ -326,9 +315,7 @@ static struct snd_soc_component_driver au1xpsc_soc_component = {
|
||||
.name = DRV_NAME,
|
||||
.open = au1xpsc_pcm_open,
|
||||
.close = au1xpsc_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = au1xpsc_pcm_hw_params,
|
||||
.hw_free = au1xpsc_pcm_hw_free,
|
||||
.prepare = au1xpsc_pcm_prepare,
|
||||
.trigger = au1xpsc_pcm_trigger,
|
||||
.pointer = au1xpsc_pcm_pointer,
|
||||
|
@ -231,19 +231,10 @@ static int alchemy_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream, component);
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = au1000_setup_dma_link(stream,
|
||||
params_period_bytes(hw_params),
|
||||
params_periods(hw_params));
|
||||
if (err)
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
|
||||
return err;
|
||||
return au1000_setup_dma_link(stream,
|
||||
params_period_bytes(hw_params),
|
||||
params_periods(hw_params));
|
||||
}
|
||||
|
||||
static int alchemy_pcm_hw_free(struct snd_soc_component *component,
|
||||
@ -251,7 +242,7 @@ static int alchemy_pcm_hw_free(struct snd_soc_component *component,
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream, component);
|
||||
au1000_release_dma_link(stream);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_trigger(struct snd_soc_component *component,
|
||||
@ -292,8 +283,8 @@ static int alchemy_pcm_new(struct snd_soc_component *component,
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
NULL, 65536, (4096 * 1024) - 1);
|
||||
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
NULL, 65536, (4096 * 1024) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -302,7 +293,6 @@ static struct snd_soc_component_driver alchemy_pcm_soc_component = {
|
||||
.name = DRV_NAME,
|
||||
.open = alchemy_pcm_open,
|
||||
.close = alchemy_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = alchemy_pcm_hw_params,
|
||||
.hw_free = alchemy_pcm_hw_free,
|
||||
.trigger = alchemy_pcm_trigger,
|
||||
|
@ -339,7 +339,6 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
|
||||
.bus_control = true,
|
||||
.probe = au1xpsc_ac97_probe,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
|
@ -818,7 +818,6 @@ static int cygnus_dma_new(struct snd_soc_component *component,
|
||||
static struct snd_soc_component_driver cygnus_soc_platform = {
|
||||
.open = cygnus_pcm_open,
|
||||
.close = cygnus_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = cygnus_pcm_hw_params,
|
||||
.hw_free = cygnus_pcm_hw_free,
|
||||
.prepare = cygnus_pcm_prepare,
|
||||
|
@ -1052,10 +1052,13 @@ static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
|
||||
static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
if (!aio->is_slave) {
|
||||
u32 val;
|
||||
|
||||
@ -1078,11 +1081,25 @@ static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
|
||||
static int cygnus_ssp_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_dai *dai;
|
||||
int ret = 0;
|
||||
|
||||
for_each_component_dais(component, dai)
|
||||
ret |= __cygnus_ssp_suspend(dai);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
|
||||
int error;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
if (!aio->is_slave) {
|
||||
if (aio->clk_trace.cap_clk_en) {
|
||||
error = clk_prepare_enable(aio->cygaud->
|
||||
@ -1109,6 +1126,18 @@ static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_ssp_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_dai *dai;
|
||||
int ret = 0;
|
||||
|
||||
for_each_component_dais(component, dai)
|
||||
ret |= __cygnus_ssp_resume(dai);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
#define cygnus_ssp_suspend NULL
|
||||
#define cygnus_ssp_resume NULL
|
||||
@ -1149,8 +1178,6 @@ static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {
|
||||
SNDRV_PCM_FMTBIT_S32_LE, \
|
||||
}, \
|
||||
.ops = &cygnus_ssp_dai_ops, \
|
||||
.suspend = cygnus_ssp_suspend, \
|
||||
.resume = cygnus_ssp_resume, \
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
|
||||
@ -1169,14 +1196,14 @@ static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.ops = &cygnus_spdif_dai_ops,
|
||||
.suspend = cygnus_ssp_suspend,
|
||||
.resume = cygnus_ssp_resume,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
|
||||
|
||||
static const struct snd_soc_component_driver cygnus_ssp_component = {
|
||||
.name = "cygnus-audio",
|
||||
.suspend = cygnus_ssp_suspend,
|
||||
.resume = cygnus_ssp_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -336,7 +336,6 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
|
||||
static struct snd_soc_dai_driver ep93xx_ac97_dai = {
|
||||
.name = "ep93xx-ac97",
|
||||
.id = 0,
|
||||
.bus_control = true,
|
||||
.probe = ep93xx_ac97_dai_probe,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
|
@ -364,11 +364,11 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
|
||||
static int ep93xx_i2s_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
|
||||
struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!dai->active)
|
||||
if (!component->active)
|
||||
return 0;
|
||||
|
||||
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
@ -377,11 +377,11 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
|
||||
static int ep93xx_i2s_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
|
||||
struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!dai->active)
|
||||
if (!component->active)
|
||||
return 0;
|
||||
|
||||
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
@ -406,8 +406,6 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
|
||||
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
|
||||
.symmetric_rates= 1,
|
||||
.probe = ep93xx_i2s_dai_probe,
|
||||
.suspend = ep93xx_i2s_suspend,
|
||||
.resume = ep93xx_i2s_resume,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
@ -425,6 +423,8 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
|
||||
|
||||
static const struct snd_soc_component_driver ep93xx_i2s_component = {
|
||||
.name = "ep93xx-i2s",
|
||||
.suspend = ep93xx_i2s_suspend,
|
||||
.resume = ep93xx_i2s_resume,
|
||||
};
|
||||
|
||||
static int ep93xx_i2s_probe(struct platform_device *pdev)
|
||||
|
@ -101,6 +101,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
select SND_SOC_JZ4725B_CODEC
|
||||
select SND_SOC_JZ4770_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
select SND_SOC_LM49453 if I2C
|
||||
select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
|
||||
@ -124,6 +125,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_ML26124 if I2C
|
||||
select SND_SOC_MT6351 if MTK_PMIC_WRAP
|
||||
select SND_SOC_MT6358 if MTK_PMIC_WRAP
|
||||
select SND_SOC_MT6660 if I2C
|
||||
select SND_SOC_NAU8540 if I2C
|
||||
select SND_SOC_NAU8810 if I2C
|
||||
select SND_SOC_NAU8822 if I2C
|
||||
@ -149,6 +151,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
select SND_SOC_RT1011 if I2C
|
||||
select SND_SOC_RT1015 if I2C
|
||||
select SND_SOC_RT1305 if I2C
|
||||
select SND_SOC_RT1308 if I2C
|
||||
select SND_SOC_RT5514 if I2C
|
||||
@ -165,6 +168,10 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_RT5670 if I2C
|
||||
select SND_SOC_RT5677 if I2C && SPI_MASTER
|
||||
select SND_SOC_RT5682 if I2C
|
||||
select SND_SOC_RT700_SDW if SOUNDWIRE
|
||||
select SND_SOC_RT711_SDW if SOUNDWIRE
|
||||
select SND_SOC_RT715_SDW if SOUNDWIRE
|
||||
select SND_SOC_RT1308_SDW if SOUNDWIRE
|
||||
select SND_SOC_SGTL5000 if I2C
|
||||
select SND_SOC_SI476X if MFD_SI476X_CORE
|
||||
select SND_SOC_SIMPLE_AMPLIFIER
|
||||
@ -207,6 +214,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_UDA134X
|
||||
select SND_SOC_UDA1380 if I2C
|
||||
select SND_SOC_WCD9335 if SLIMBUS
|
||||
select SND_SOC_WCD934X if MFD_WCD934X
|
||||
select SND_SOC_WL1273 if MFD_WL1273_CORE
|
||||
select SND_SOC_WM0010 if SPI_MASTER
|
||||
select SND_SOC_WM1250_EV1 if I2C
|
||||
@ -261,6 +269,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_WSA881X if SOUNDWIRE
|
||||
help
|
||||
Normally ASoC codec drivers are only built if a machine driver which
|
||||
uses them is also built since they are only usable with a machine
|
||||
@ -672,6 +681,17 @@ config SND_SOC_JZ4725B_CODEC
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called snd-soc-jz4725b-codec.
|
||||
|
||||
config SND_SOC_JZ4770_CODEC
|
||||
depends on MIPS || COMPILE_TEST
|
||||
select REGMAP
|
||||
tristate "Ingenic JZ4770 internal CODEC"
|
||||
help
|
||||
Enable support for the internal CODEC found in the JZ4770 SoC
|
||||
from Ingenic.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called snd-soc-jz4770-codec.
|
||||
|
||||
config SND_SOC_L3
|
||||
tristate
|
||||
|
||||
@ -951,6 +971,7 @@ config SND_SOC_RL6231
|
||||
default y if SND_SOC_RT5677=y
|
||||
default y if SND_SOC_RT5682=y
|
||||
default y if SND_SOC_RT1011=y
|
||||
default y if SND_SOC_RT1015=y
|
||||
default y if SND_SOC_RT1305=y
|
||||
default y if SND_SOC_RT1308=y
|
||||
default m if SND_SOC_RT5514=m
|
||||
@ -967,6 +988,7 @@ config SND_SOC_RL6231
|
||||
default m if SND_SOC_RT5677=m
|
||||
default m if SND_SOC_RT5682=m
|
||||
default m if SND_SOC_RT1011=m
|
||||
default m if SND_SOC_RT1015=m
|
||||
default m if SND_SOC_RT1305=m
|
||||
default m if SND_SOC_RT1308=m
|
||||
|
||||
@ -994,12 +1016,20 @@ config SND_SOC_RT298
|
||||
config SND_SOC_RT1011
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT1015
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT1305
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT1308
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT1308_SDW
|
||||
tristate "Realtek RT1308 Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
select REGMAP_SOUNDWIRE
|
||||
|
||||
config SND_SOC_RT5514
|
||||
tristate
|
||||
|
||||
@ -1057,6 +1087,33 @@ config SND_SOC_RT5677_SPI
|
||||
config SND_SOC_RT5682
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT700
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT700_SDW
|
||||
tristate "Realtek RT700 Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
select SND_SOC_RT700
|
||||
select REGMAP_SOUNDWIRE
|
||||
|
||||
config SND_SOC_RT711
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT711_SDW
|
||||
tristate "Realtek RT711 Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
select SND_SOC_RT711
|
||||
select REGMAP_SOUNDWIRE
|
||||
|
||||
config SND_SOC_RT715
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT715_SDW
|
||||
tristate "Realtek RT715 Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
select SND_SOC_RT715
|
||||
select REGMAP_SOUNDWIRE
|
||||
|
||||
#Freescale sgtl5000 codec
|
||||
config SND_SOC_SGTL5000
|
||||
tristate "Freescale SGTL5000 CODEC"
|
||||
@ -1275,6 +1332,13 @@ config SND_SOC_WCD9335
|
||||
Qualcomm Technologies, Inc. (QTI) multimedia solutions,
|
||||
including the MSM8996, MSM8976, and MSM8956 chipsets.
|
||||
|
||||
config SND_SOC_WCD934X
|
||||
tristate "WCD9340/WCD9341 Codec"
|
||||
depends on MFD_WCD934X
|
||||
help
|
||||
The WCD9340/9341 is a audio codec IC Integrated in
|
||||
Qualcomm SoCs like SDM845.
|
||||
|
||||
config SND_SOC_WL1273
|
||||
tristate
|
||||
|
||||
@ -1473,6 +1537,15 @@ config SND_SOC_WM9713
|
||||
select REGMAP_AC97
|
||||
select AC97_BUS_COMPAT if AC97_BUS_NEW
|
||||
|
||||
config SND_SOC_WSA881X
|
||||
tristate "WSA881X Codec"
|
||||
depends on SOUNDWIRE
|
||||
select REGMAP_SOUNDWIRE
|
||||
tristate
|
||||
help
|
||||
This enables support for Qualcomm WSA8810/WSA8815 Class-D
|
||||
Smart Speaker Amplifier.
|
||||
|
||||
config SND_SOC_ZX_AUD96P22
|
||||
tristate "ZTE ZX AUD96P22 CODEC"
|
||||
depends on I2C
|
||||
@ -1507,6 +1580,15 @@ config SND_SOC_MT6358
|
||||
Enable support for the platform which uses MT6358 as
|
||||
external codec device.
|
||||
|
||||
config SND_SOC_MT6660
|
||||
tristate "Mediatek MT6660 Speaker Amplifier"
|
||||
depends on I2C
|
||||
help
|
||||
MediaTek MT6660 is a smart power amplifier which contain
|
||||
speaker protection, multi-band DRC, equalizer functions.
|
||||
Select N if you don't have MT6660 on board.
|
||||
Select M to build this as module.
|
||||
|
||||
config SND_SOC_NAU8540
|
||||
tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
|
||||
depends on I2C
|
||||
|
@ -97,6 +97,7 @@ snd-soc-inno-rk3036-objs := inno_rk3036.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
snd-soc-jz4725b-codec-objs := jz4725b.o
|
||||
snd-soc-jz4770-codec-objs := jz4770.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
snd-soc-lm4857-objs := lm4857.o
|
||||
snd-soc-lm49453-objs := lm49453.o
|
||||
@ -122,6 +123,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
|
||||
snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
|
||||
snd-soc-mt6351-objs := mt6351.o
|
||||
snd-soc-mt6358-objs := mt6358.o
|
||||
snd-soc-mt6660-objs := mt6660.o
|
||||
snd-soc-nau8540-objs := nau8540.o
|
||||
snd-soc-nau8810-objs := nau8810.o
|
||||
snd-soc-nau8822-objs := nau8822.o
|
||||
@ -152,8 +154,10 @@ snd-soc-rk3328-objs := rk3328_codec.o
|
||||
snd-soc-rl6231-objs := rl6231.o
|
||||
snd-soc-rl6347a-objs := rl6347a.o
|
||||
snd-soc-rt1011-objs := rt1011.o
|
||||
snd-soc-rt1015-objs := rt1015.o
|
||||
snd-soc-rt1305-objs := rt1305.o
|
||||
snd-soc-rt1308-objs := rt1308.o
|
||||
snd-soc-rt1308-sdw-objs := rt1308-sdw.o
|
||||
snd-soc-rt274-objs := rt274.o
|
||||
snd-soc-rt286-objs := rt286.o
|
||||
snd-soc-rt298-objs := rt298.o
|
||||
@ -173,6 +177,9 @@ snd-soc-rt5670-objs := rt5670.o
|
||||
snd-soc-rt5677-objs := rt5677.o
|
||||
snd-soc-rt5677-spi-objs := rt5677-spi.o
|
||||
snd-soc-rt5682-objs := rt5682.o
|
||||
snd-soc-rt700-objs := rt700.o rt700-sdw.o
|
||||
snd-soc-rt711-objs := rt711.o rt711-sdw.o
|
||||
snd-soc-rt715-objs := rt715.o rt715-sdw.o
|
||||
snd-soc-sgtl5000-objs := sgtl5000.o
|
||||
snd-soc-alc5623-objs := alc5623.o
|
||||
snd-soc-alc5632-objs := alc5632.o
|
||||
@ -220,6 +227,7 @@ snd-soc-uda1334-objs := uda1334.o
|
||||
snd-soc-uda134x-objs := uda134x.o
|
||||
snd-soc-uda1380-objs := uda1380.o
|
||||
snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o
|
||||
snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o
|
||||
snd-soc-wl1273-objs := wl1273.o
|
||||
snd-soc-wm-adsp-objs := wm_adsp.o
|
||||
snd-soc-wm0010-objs := wm0010.o
|
||||
@ -277,6 +285,7 @@ snd-soc-wm9705-objs := wm9705.o
|
||||
snd-soc-wm9712-objs := wm9712.o
|
||||
snd-soc-wm9713-objs := wm9713.o
|
||||
snd-soc-wm-hubs-objs := wm_hubs.o
|
||||
snd-soc-wsa881x-objs := wsa881x.o
|
||||
snd-soc-zx-aud96p22-objs := zx_aud96p22.o
|
||||
# Amp
|
||||
snd-soc-max9877-objs := max9877.o
|
||||
@ -386,6 +395,7 @@ obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
|
||||
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
|
||||
@ -411,6 +421,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
|
||||
obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o
|
||||
obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o
|
||||
obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o
|
||||
@ -441,8 +452,10 @@ obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
|
||||
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
||||
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
||||
obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
|
||||
obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
|
||||
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
|
||||
obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
|
||||
obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o
|
||||
obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o
|
||||
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
|
||||
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
|
||||
@ -463,6 +476,9 @@ obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
|
||||
obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o
|
||||
obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
|
||||
obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
|
||||
obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
|
||||
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
|
||||
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
|
||||
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
|
||||
@ -509,6 +525,7 @@ obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
|
||||
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
|
||||
obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
|
||||
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
|
||||
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
|
||||
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
|
||||
@ -566,6 +583,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
|
||||
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
|
||||
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
|
||||
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
|
||||
obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
|
||||
obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o
|
||||
|
||||
# Amp
|
||||
|
@ -919,7 +919,7 @@ static int wov_pcm_hw_params(struct snd_soc_component *component,
|
||||
priv->wov_burst_read = true;
|
||||
mutex_unlock(&priv->wov_dma_lock);
|
||||
|
||||
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wov_pcm_hw_free(struct snd_soc_component *component,
|
||||
@ -935,7 +935,7 @@ static int wov_pcm_hw_free(struct snd_soc_component *component,
|
||||
|
||||
cancel_delayed_work_sync(&priv->wov_copy_work);
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
|
||||
@ -951,8 +951,8 @@ static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
|
||||
static int wov_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -214,12 +214,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
|
||||
CS42L51_POWER_CTL1, 2, 1,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
|
||||
CS42L51_POWER_CTL1, 5, 1,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
|
||||
CS42L51_POWER_CTL1, 6, 1,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
/* analog/mic */
|
||||
SND_SOC_DAPM_INPUT("AIN1L"),
|
||||
@ -255,6 +253,12 @@ static const struct snd_soc_dapm_route cs42l51_routes[] = {
|
||||
{"HPL", NULL, "Left DAC"},
|
||||
{"HPR", NULL, "Right DAC"},
|
||||
|
||||
{"Right DAC", NULL, "DAC Mux"},
|
||||
{"Left DAC", NULL, "DAC Mux"},
|
||||
|
||||
{"DAC Mux", "Direct PCM", "Playback"},
|
||||
{"DAC Mux", "DSP PCM", "Playback"},
|
||||
|
||||
{"Left ADC", NULL, "Left PGA"},
|
||||
{"Right ADC", NULL, "Right PGA"},
|
||||
|
||||
|
@ -438,11 +438,13 @@ static const struct snd_kcontrol_new cs47l15_aec_loopback_mux[] = {
|
||||
static const struct snd_soc_dapm_widget cs47l15_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
0, madera_sysclk_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1,
|
||||
MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
|
||||
|
@ -521,11 +521,13 @@ static const struct snd_kcontrol_new cs47l35_aec_loopback_mux[] = {
|
||||
static const struct snd_soc_dapm_widget cs47l35_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
0, madera_sysclk_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, NULL, 0),
|
||||
0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
|
||||
|
@ -790,15 +790,18 @@ static const struct snd_kcontrol_new cs47l85_output_anc_src[] = {
|
||||
static const struct snd_soc_dapm_widget cs47l85_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
0, madera_sysclk_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
|
||||
MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1,
|
||||
MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
|
||||
|
@ -744,15 +744,18 @@ static const struct snd_kcontrol_new cs47l90_output_anc_src[] = {
|
||||
static const struct snd_soc_dapm_widget cs47l90_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
0, madera_sysclk_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
|
||||
MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1,
|
||||
MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
|
||||
|
@ -163,6 +163,51 @@ static int cs47l92_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
||||
return wm_adsp_early_event(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static int cs47l92_outclk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
|
||||
struct madera_priv *priv = &cs47l92->core;
|
||||
struct madera *madera = priv->madera;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(madera->regmap, MADERA_OUTPUT_RATE_1, &val);
|
||||
if (ret) {
|
||||
dev_err(madera->dev, "Failed to read OUTCLK source: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
val &= MADERA_OUT_CLK_SRC_MASK;
|
||||
|
||||
switch (val) {
|
||||
case MADERA_OUTCLK_MCLK1:
|
||||
case MADERA_OUTCLK_MCLK2:
|
||||
case MADERA_OUTCLK_MCLK3:
|
||||
val -= (MADERA_OUTCLK_MCLK1 - MADERA_MCLK1);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
ret = clk_prepare_enable(madera->mclk[val].clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
clk_disable_unprepare(madera->mclk[val].clk);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return madera_domain_clk_ev(w, kcontrol, event);
|
||||
}
|
||||
|
||||
#define CS47L92_NG_SRC(name, base) \
|
||||
SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
|
||||
SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
|
||||
@ -615,15 +660,18 @@ static const struct snd_kcontrol_new cs47l92_aec_loopback_mux =
|
||||
static const struct snd_soc_dapm_widget cs47l92_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
0, madera_sysclk_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
|
||||
MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1,
|
||||
MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, madera_clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
|
||||
@ -666,7 +714,7 @@ SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
|
||||
MADERA_DOM_GRP_OUT, 0,
|
||||
madera_domain_clk_ev,
|
||||
cs47l92_outclk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
|
||||
MADERA_DOM_GRP_SPD, 0,
|
||||
@ -1971,10 +2019,8 @@ static int cs47l92_probe(struct platform_device *pdev)
|
||||
goto error_dsp_irq;
|
||||
|
||||
ret = madera_init_bus_error_irq(&cs47l92->core, 0, wm_adsp2_bus_error);
|
||||
if (ret != 0) {
|
||||
wm_adsp2_remove(&cs47l92->core.adsp[0]);
|
||||
if (ret != 0)
|
||||
goto error_adsp;
|
||||
}
|
||||
|
||||
madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
|
||||
&cs47l92->fll[0]);
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
@ -47,6 +47,24 @@ static struct snd_soc_dai_driver gtm601_dai = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver bm818_dai = {
|
||||
.name = "bm818",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_gtm601 = {
|
||||
.dapm_widgets = gtm601_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
|
||||
@ -60,17 +78,21 @@ static const struct snd_soc_component_driver soc_component_dev_gtm601 = {
|
||||
|
||||
static int gtm601_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct snd_soc_dai_driver *dai_driver;
|
||||
|
||||
dai_driver = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
return devm_snd_soc_register_component(&pdev->dev,
|
||||
&soc_component_dev_gtm601, >m601_dai, 1);
|
||||
&soc_component_dev_gtm601,
|
||||
(struct snd_soc_dai_driver *)dai_driver, 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id gtm601_codec_of_match[] = {
|
||||
{ .compatible = "option,gtm601", },
|
||||
{ .compatible = "option,gtm601", .data = (void *)>m601_dai },
|
||||
{ .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gtm601_codec_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver gtm601_codec_driver = {
|
||||
.driver = {
|
||||
|
@ -164,6 +164,19 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
|
||||
.sig_bits = 24,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = HDAC_HDMI_3_DAI_ID,
|
||||
.name = "intel-hdmi-hifi4",
|
||||
.ops = &hdac_hda_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "hifi4",
|
||||
.channels_min = 1,
|
||||
.channels_max = 32,
|
||||
.rates = STUB_HDMI_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@ -346,6 +359,9 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||
case HDAC_HDMI_2_DAI_ID:
|
||||
pcm_name = "HDMI 2";
|
||||
break;
|
||||
case HDAC_HDMI_3_DAI_ID:
|
||||
pcm_name = "HDMI 3";
|
||||
break;
|
||||
default:
|
||||
dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
|
||||
return NULL;
|
||||
|
@ -13,7 +13,8 @@ enum {
|
||||
HDAC_HDMI_0_DAI_ID,
|
||||
HDAC_HDMI_1_DAI_ID,
|
||||
HDAC_HDMI_2_DAI_ID,
|
||||
HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
|
||||
HDAC_HDMI_3_DAI_ID,
|
||||
HDAC_LAST_DAI_ID = HDAC_HDMI_3_DAI_ID,
|
||||
};
|
||||
|
||||
struct hdac_hda_pcm {
|
||||
|
@ -115,16 +115,8 @@ struct hdac_hdmi_dai_port_map {
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
};
|
||||
|
||||
/*
|
||||
* pin to port mapping table where the value indicate the pin number and
|
||||
* the index indicate the port number with 1 base.
|
||||
*/
|
||||
static const int icl_pin2port_map[] = {0x4, 0x6, 0x8, 0xa, 0xb};
|
||||
|
||||
struct hdac_hdmi_drv_data {
|
||||
unsigned int vendor_nid;
|
||||
const int *port_map; /* pin to port mapping table */
|
||||
int port_num;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_priv {
|
||||
@ -1374,12 +1366,11 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define INTEL_VENDOR_NID_0x2 0x02
|
||||
#define INTEL_VENDOR_NID_0x8 0x08
|
||||
#define INTEL_VENDOR_NID_0xb 0x0b
|
||||
#define INTEL_VENDOR_NID 0x08
|
||||
#define INTEL_GLK_VENDOR_NID 0x0b
|
||||
#define INTEL_GET_VENDOR_VERB 0xf81
|
||||
#define INTEL_SET_VENDOR_VERB 0x781
|
||||
#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
|
||||
#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
|
||||
#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
|
||||
|
||||
static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev)
|
||||
@ -1566,26 +1557,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev,
|
||||
|
||||
static int hdac_hdmi_pin2port(void *aptr, int pin)
|
||||
{
|
||||
struct hdac_device *hdev = aptr;
|
||||
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
|
||||
const int *map = hdmi->drv_data->port_map;
|
||||
int i;
|
||||
|
||||
if (!hdmi->drv_data->port_num)
|
||||
return pin - 4; /* map NID 0x05 -> port #1 */
|
||||
|
||||
/*
|
||||
* looking for the pin number in the mapping table and return
|
||||
* the index which indicate the port number
|
||||
*/
|
||||
for (i = 0; i < hdmi->drv_data->port_num; i++) {
|
||||
if (pin == map[i])
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/* return -1 if pin number exceeds our expectation */
|
||||
dev_err(&hdev->dev, "Can't find the port for pin %d\n", pin);
|
||||
return -1;
|
||||
return pin - 4; /* map NID 0x05 -> port #1 */
|
||||
}
|
||||
|
||||
static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
|
||||
@ -1596,18 +1568,9 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
|
||||
struct hdac_hdmi_port *hport = NULL;
|
||||
struct snd_soc_component *component = hdmi->component;
|
||||
int i;
|
||||
hda_nid_t pin_nid;
|
||||
|
||||
if (!hdmi->drv_data->port_num) {
|
||||
/* for legacy platforms */
|
||||
pin_nid = port + 0x04;
|
||||
} else if (port < hdmi->drv_data->port_num) {
|
||||
/* get pin number from the pin2port mapping table */
|
||||
pin_nid = hdmi->drv_data->port_map[port - 1];
|
||||
} else {
|
||||
dev_err(&hdev->dev, "Can't find the pin for port %d\n", port);
|
||||
return;
|
||||
}
|
||||
/* Don't know how this mapping is derived */
|
||||
hda_nid_t pin_nid = port + 0x04;
|
||||
|
||||
dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__,
|
||||
pin_nid, pipe);
|
||||
@ -2025,18 +1988,12 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
|
||||
return port->eld.info.spk_alloc;
|
||||
}
|
||||
|
||||
static struct hdac_hdmi_drv_data intel_icl_drv_data = {
|
||||
.vendor_nid = INTEL_VENDOR_NID_0x2,
|
||||
.port_map = icl_pin2port_map,
|
||||
.port_num = ARRAY_SIZE(icl_pin2port_map),
|
||||
};
|
||||
|
||||
static struct hdac_hdmi_drv_data intel_glk_drv_data = {
|
||||
.vendor_nid = INTEL_VENDOR_NID_0xb,
|
||||
.vendor_nid = INTEL_GLK_VENDOR_NID,
|
||||
};
|
||||
|
||||
static struct hdac_hdmi_drv_data intel_drv_data = {
|
||||
.vendor_nid = INTEL_VENDOR_NID_0x8,
|
||||
.vendor_nid = INTEL_VENDOR_NID,
|
||||
};
|
||||
|
||||
static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
|
||||
@ -2216,8 +2173,6 @@ static const struct hda_device_id hdmi_list[] = {
|
||||
&intel_glk_drv_data),
|
||||
HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI",
|
||||
&intel_glk_drv_data),
|
||||
HDA_CODEC_EXT_ENTRY(0x8086280f, 0x100000, "Icelake HDMI",
|
||||
&intel_icl_drv_data),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -274,7 +274,8 @@ struct hdmi_codec_priv {
|
||||
uint8_t eld[MAX_ELD_BYTES];
|
||||
struct snd_pcm_chmap *chmap_info;
|
||||
unsigned int chmap_idx;
|
||||
unsigned long busy;
|
||||
struct mutex lock;
|
||||
bool busy;
|
||||
struct snd_soc_jack *jack;
|
||||
unsigned int jack_status;
|
||||
};
|
||||
@ -390,9 +391,10 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
|
||||
ret = test_and_set_bit(0, &hcp->busy);
|
||||
if (ret) {
|
||||
mutex_lock(&hcp->lock);
|
||||
if (hcp->busy) {
|
||||
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
|
||||
mutex_unlock(&hcp->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -405,21 +407,21 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
if (hcp->hcd.ops->get_eld) {
|
||||
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
|
||||
hcp->eld, sizeof(hcp->eld));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!ret) {
|
||||
ret = snd_pcm_hw_constraint_eld(substream->runtime,
|
||||
hcp->eld);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
/* Select chmap supported */
|
||||
hdmi_codec_eld_chmap(hcp);
|
||||
}
|
||||
return 0;
|
||||
|
||||
hcp->busy = true;
|
||||
|
||||
err:
|
||||
/* Release the exclusive lock on error */
|
||||
clear_bit(0, &hcp->busy);
|
||||
mutex_unlock(&hcp->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -431,7 +433,9 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
|
||||
|
||||
clear_bit(0, &hcp->busy);
|
||||
mutex_lock(&hcp->lock);
|
||||
hcp->busy = false;
|
||||
mutex_unlock(&hcp->lock);
|
||||
}
|
||||
|
||||
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
@ -811,6 +815,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
hcp->hcd = *hcd;
|
||||
mutex_init(&hcp->lock);
|
||||
|
||||
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
|
||||
if (!daidrv)
|
||||
return -ENOMEM;
|
||||
|
948
sound/soc/codecs/jz4770.c
Normal file
948
sound/soc/codecs/jz4770.c
Normal file
@ -0,0 +1,948 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Ingenic JZ4770 CODEC driver
|
||||
//
|
||||
// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org>
|
||||
// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#define ICDC_RGADW_OFFSET 0x00
|
||||
#define ICDC_RGDATA_OFFSET 0x04
|
||||
|
||||
/* ICDC internal register access control register(RGADW) */
|
||||
#define ICDC_RGADW_RGWR BIT(16)
|
||||
|
||||
#define ICDC_RGADW_RGADDR_OFFSET 8
|
||||
#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
|
||||
|
||||
#define ICDC_RGADW_RGDIN_OFFSET 0
|
||||
#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
|
||||
|
||||
/* ICDC internal register data output register (RGDATA)*/
|
||||
#define ICDC_RGDATA_IRQ BIT(8)
|
||||
|
||||
#define ICDC_RGDATA_RGDOUT_OFFSET 0
|
||||
#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
|
||||
|
||||
/* Internal register space, accessed through regmap */
|
||||
enum {
|
||||
JZ4770_CODEC_REG_SR,
|
||||
JZ4770_CODEC_REG_AICR_DAC,
|
||||
JZ4770_CODEC_REG_AICR_ADC,
|
||||
JZ4770_CODEC_REG_CR_LO,
|
||||
JZ4770_CODEC_REG_CR_HP,
|
||||
|
||||
JZ4770_CODEC_REG_MISSING_REG1,
|
||||
|
||||
JZ4770_CODEC_REG_CR_DAC,
|
||||
JZ4770_CODEC_REG_CR_MIC,
|
||||
JZ4770_CODEC_REG_CR_LI,
|
||||
JZ4770_CODEC_REG_CR_ADC,
|
||||
JZ4770_CODEC_REG_CR_MIX,
|
||||
JZ4770_CODEC_REG_CR_VIC,
|
||||
JZ4770_CODEC_REG_CCR,
|
||||
JZ4770_CODEC_REG_FCR_DAC,
|
||||
JZ4770_CODEC_REG_FCR_ADC,
|
||||
JZ4770_CODEC_REG_ICR,
|
||||
JZ4770_CODEC_REG_IMR,
|
||||
JZ4770_CODEC_REG_IFR,
|
||||
JZ4770_CODEC_REG_GCR_HPL,
|
||||
JZ4770_CODEC_REG_GCR_HPR,
|
||||
JZ4770_CODEC_REG_GCR_LIBYL,
|
||||
JZ4770_CODEC_REG_GCR_LIBYR,
|
||||
JZ4770_CODEC_REG_GCR_DACL,
|
||||
JZ4770_CODEC_REG_GCR_DACR,
|
||||
JZ4770_CODEC_REG_GCR_MIC1,
|
||||
JZ4770_CODEC_REG_GCR_MIC2,
|
||||
JZ4770_CODEC_REG_GCR_ADCL,
|
||||
JZ4770_CODEC_REG_GCR_ADCR,
|
||||
|
||||
JZ4770_CODEC_REG_MISSING_REG2,
|
||||
|
||||
JZ4770_CODEC_REG_GCR_MIXADC,
|
||||
JZ4770_CODEC_REG_GCR_MIXDAC,
|
||||
JZ4770_CODEC_REG_AGC1,
|
||||
JZ4770_CODEC_REG_AGC2,
|
||||
JZ4770_CODEC_REG_AGC3,
|
||||
JZ4770_CODEC_REG_AGC4,
|
||||
JZ4770_CODEC_REG_AGC5,
|
||||
};
|
||||
|
||||
#define REG_AICR_DAC_ADWL_OFFSET 6
|
||||
#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET)
|
||||
#define REG_AICR_DAC_SERIAL BIT(1)
|
||||
#define REG_AICR_DAC_I2S BIT(0)
|
||||
|
||||
#define REG_AICR_ADC_ADWL_OFFSET 6
|
||||
#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET)
|
||||
#define REG_AICR_ADC_SERIAL BIT(1)
|
||||
#define REG_AICR_ADC_I2S BIT(0)
|
||||
|
||||
#define REG_CR_LO_MUTE_OFFSET 7
|
||||
#define REG_CR_LO_SB_OFFSET 4
|
||||
#define REG_CR_LO_SEL_OFFSET 0
|
||||
#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET)
|
||||
|
||||
#define REG_CR_HP_MUTE BIT(7)
|
||||
#define REG_CR_HP_LOAD BIT(6)
|
||||
#define REG_CR_HP_SB_OFFSET 4
|
||||
#define REG_CR_HP_SB_HPCM BIT(3)
|
||||
#define REG_CR_HP_SEL_OFFSET 0
|
||||
#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET)
|
||||
|
||||
#define REG_CR_DAC_MUTE BIT(7)
|
||||
#define REG_CR_DAC_MONO BIT(6)
|
||||
#define REG_CR_DAC_LEFT_ONLY BIT(5)
|
||||
#define REG_CR_DAC_SB_OFFSET 4
|
||||
#define REG_CR_DAC_LRSWAP BIT(3)
|
||||
|
||||
#define REG_CR_MIC_STEREO_OFFSET 7
|
||||
#define REG_CR_MIC_IDIFF_OFFSET 6
|
||||
#define REG_CR_MIC_SB_MIC2_OFFSET 5
|
||||
#define REG_CR_MIC_SB_MIC1_OFFSET 4
|
||||
#define REG_CR_MIC_BIAS_V0_OFFSET 1
|
||||
#define REG_CR_MIC_BIAS_SB_OFFSET 0
|
||||
|
||||
#define REG_CR_LI_LIBY_OFFSET 4
|
||||
#define REG_CR_LI_SB_OFFSET 0
|
||||
|
||||
#define REG_CR_ADC_DMIC_SEL BIT(7)
|
||||
#define REG_CR_ADC_MONO BIT(6)
|
||||
#define REG_CR_ADC_LEFT_ONLY BIT(5)
|
||||
#define REG_CR_ADC_SB_OFFSET 4
|
||||
#define REG_CR_ADC_LRSWAP BIT(3)
|
||||
#define REG_CR_ADC_IN_SEL_OFFSET 0
|
||||
#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET)
|
||||
|
||||
#define REG_CR_VIC_SB_SLEEP BIT(1)
|
||||
#define REG_CR_VIC_SB BIT(0)
|
||||
|
||||
#define REG_CCR_CRYSTAL_OFFSET 0
|
||||
#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET)
|
||||
|
||||
#define REG_FCR_DAC_FREQ_OFFSET 0
|
||||
#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET)
|
||||
|
||||
#define REG_FCR_ADC_FREQ_OFFSET 0
|
||||
#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET)
|
||||
|
||||
#define REG_ICR_INT_FORM_OFFSET 6
|
||||
#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET)
|
||||
|
||||
#define REG_IMR_ALL_MASK (0x7f)
|
||||
#define REG_IMR_SCLR_MASK BIT(6)
|
||||
#define REG_IMR_JACK_MASK BIT(5)
|
||||
#define REG_IMR_SCMC_MASK BIT(4)
|
||||
#define REG_IMR_RUP_MASK BIT(3)
|
||||
#define REG_IMR_RDO_MASK BIT(2)
|
||||
#define REG_IMR_GUP_MASK BIT(1)
|
||||
#define REG_IMR_GDO_MASK BIT(0)
|
||||
|
||||
#define REG_IFR_ALL_MASK (0x7f)
|
||||
#define REG_IFR_SCLR BIT(6)
|
||||
#define REG_IFR_JACK BIT(5)
|
||||
#define REG_IFR_SCMC BIT(4)
|
||||
#define REG_IFR_RUP BIT(3)
|
||||
#define REG_IFR_RDO BIT(2)
|
||||
#define REG_IFR_GUP BIT(1)
|
||||
#define REG_IFR_GDO BIT(0)
|
||||
|
||||
#define REG_GCR_HPL_LRGO BIT(7)
|
||||
|
||||
#define REG_GCR_DACL_RLGOD BIT(7)
|
||||
|
||||
#define REG_GCR_GAIN_OFFSET 0
|
||||
#define REG_GCR_GAIN_MAX 0x1f
|
||||
|
||||
#define REG_GCR_MIC_GAIN_OFFSET 0
|
||||
#define REG_GCR_MIC_GAIN_MAX 5
|
||||
|
||||
#define REG_GCR_ADC_GAIN_OFFSET 0
|
||||
#define REG_GCR_ADC_GAIN_MAX 23
|
||||
|
||||
#define REG_AGC1_EN BIT(7)
|
||||
|
||||
/* codec private data */
|
||||
struct jz_codec {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int jz4770_codec_set_bias_level(struct snd_soc_component *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
struct regmap *regmap = jz_codec->regmap;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
|
||||
REG_CR_VIC_SB, 0);
|
||||
msleep(250);
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
|
||||
REG_CR_VIC_SB_SLEEP, 0);
|
||||
msleep(400);
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
|
||||
REG_CR_VIC_SB_SLEEP, REG_CR_VIC_SB_SLEEP);
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
|
||||
REG_CR_VIC_SB, REG_CR_VIC_SB);
|
||||
/* fall-through */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4770_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *codec = dai->component;
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
|
||||
|
||||
/*
|
||||
* SYSCLK output from the codec to the AIC is required to keep the
|
||||
* DMA transfer going during playback when all audible outputs have
|
||||
* been disabled.
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4770_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *codec = dai->component;
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_soc_dapm_disable_pin(dapm, "SYSCLK");
|
||||
}
|
||||
|
||||
|
||||
static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *codec = dai->component;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_soc_component_force_bias_level(codec,
|
||||
SND_SOC_BIAS_ON);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
/* do nothing */
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jz4770_codec_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_component *codec = dai->component;
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
|
||||
unsigned int val;
|
||||
int change, err;
|
||||
|
||||
change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC,
|
||||
REG_CR_DAC_MUTE,
|
||||
mute ? REG_CR_DAC_MUTE : 0);
|
||||
if (change == 1) {
|
||||
regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val);
|
||||
|
||||
if (val & BIT(REG_CR_DAC_SB_OFFSET))
|
||||
return 1;
|
||||
|
||||
err = regmap_read_poll_timeout(jz_codec->regmap,
|
||||
JZ4770_CODEC_REG_IFR,
|
||||
val, val & gain_bit,
|
||||
1000, 100 * USEC_PER_MSEC);
|
||||
if (err) {
|
||||
dev_err(jz_codec->dev,
|
||||
"Timeout while setting digital mute: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* clear GUP/GDO flag */
|
||||
regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
|
||||
gain_bit, gain_bit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unit: 0.01dB */
|
||||
static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
|
||||
static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 400, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
|
||||
|
||||
/* Unconditional controls. */
|
||||
static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
|
||||
/* record gain control */
|
||||
SOC_DOUBLE_R_TLV("PCM Capture Volume",
|
||||
JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
|
||||
REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
|
||||
0, adc_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
|
||||
JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
|
||||
REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Volume",
|
||||
.info = snd_soc_info_volsw,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
|
||||
| SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.tlv.p = dac_tlv,
|
||||
.get = snd_soc_dapm_get_volsw,
|
||||
.put = snd_soc_dapm_put_volsw,
|
||||
/*
|
||||
* NOTE: DACR/DACL are inversed; the gain value written to DACR
|
||||
* seems to affect the left channel, and the gain value written
|
||||
* to DACL seems to affect the right channel.
|
||||
*/
|
||||
.private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR,
|
||||
JZ4770_CODEC_REG_GCR_DACL,
|
||||
REG_GCR_GAIN_OFFSET,
|
||||
REG_GCR_GAIN_MAX, 1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Volume",
|
||||
.info = snd_soc_info_volsw,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
|
||||
| SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.tlv.p = out_tlv,
|
||||
.get = snd_soc_dapm_get_volsw,
|
||||
.put = snd_soc_dapm_put_volsw,
|
||||
/* HPR/HPL inversed for the same reason as above */
|
||||
.private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR,
|
||||
JZ4770_CODEC_REG_GCR_HPL,
|
||||
REG_GCR_GAIN_OFFSET,
|
||||
REG_GCR_GAIN_MAX, 1),
|
||||
},
|
||||
};
|
||||
|
||||
static int hpout_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* set cap-less, unmute HP */
|
||||
regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, 0);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
/* wait for ramp-up complete (RUP) */
|
||||
err = regmap_read_poll_timeout(jz_codec->regmap,
|
||||
JZ4770_CODEC_REG_IFR,
|
||||
val, val & REG_IFR_RUP,
|
||||
1000, 100 * USEC_PER_MSEC);
|
||||
if (err) {
|
||||
dev_err(jz_codec->dev, "RUP timeout: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* clear RUP flag */
|
||||
regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
|
||||
REG_IFR_RUP, REG_IFR_RUP);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
/* set cap-couple, mute HP */
|
||||
regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE,
|
||||
REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE);
|
||||
|
||||
err = regmap_read_poll_timeout(jz_codec->regmap,
|
||||
JZ4770_CODEC_REG_IFR,
|
||||
val, val & REG_IFR_RDO,
|
||||
1000, 100 * USEC_PER_MSEC);
|
||||
if (err) {
|
||||
dev_err(jz_codec->dev, "RDO timeout: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* clear RDO flag */
|
||||
regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
|
||||
REG_IFR_RDO, REG_IFR_RDO);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_poweron_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (event == SND_SOC_DAPM_POST_PMU)
|
||||
msleep(1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const jz4770_codec_hp_texts[] = {
|
||||
"PCM", "Line In", "Mic 1", "Mic 2"
|
||||
};
|
||||
static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
|
||||
JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SEL_OFFSET,
|
||||
REG_CR_HP_SEL_MASK,
|
||||
jz4770_codec_hp_texts,
|
||||
jz4770_codec_hp_values);
|
||||
static const struct snd_kcontrol_new jz4770_codec_hp_source =
|
||||
SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
|
||||
JZ4770_CODEC_REG_CR_LO,
|
||||
REG_CR_LO_SEL_OFFSET,
|
||||
REG_CR_LO_SEL_MASK,
|
||||
jz4770_codec_hp_texts,
|
||||
jz4770_codec_hp_values);
|
||||
static const struct snd_kcontrol_new jz4770_codec_lo_source =
|
||||
SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
|
||||
|
||||
static const char * const jz4770_codec_cap_texts[] = {
|
||||
"Line In", "Mic 1", "Mic 2"
|
||||
};
|
||||
static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
|
||||
JZ4770_CODEC_REG_CR_ADC,
|
||||
REG_CR_ADC_IN_SEL_OFFSET,
|
||||
REG_CR_ADC_IN_SEL_MASK,
|
||||
jz4770_codec_cap_texts,
|
||||
jz4770_codec_cap_values);
|
||||
static const struct snd_kcontrol_new jz4770_codec_cap_source =
|
||||
SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
|
||||
|
||||
static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
|
||||
SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
|
||||
REG_CR_MIC_STEREO_OFFSET, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
|
||||
REG_CR_LO_SB_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
|
||||
REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
|
||||
REG_CR_LI_SB_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
|
||||
&jz4770_codec_hp_source),
|
||||
SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
|
||||
&jz4770_codec_cap_source),
|
||||
SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
|
||||
&jz4770_codec_lo_source),
|
||||
|
||||
SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
|
||||
REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
|
||||
REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
|
||||
REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
|
||||
jz4770_codec_mic_controls,
|
||||
ARRAY_SIZE(jz4770_codec_mic_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
|
||||
REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
|
||||
REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
|
||||
REG_CR_DAC_SB_OFFSET, 1),
|
||||
|
||||
SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
|
||||
jz4770_codec_pcm_playback_controls,
|
||||
ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
|
||||
SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
|
||||
jz4770_codec_hp_playback_controls,
|
||||
ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
|
||||
REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_INPUT("MIC1P"),
|
||||
SND_SOC_DAPM_INPUT("MIC1N"),
|
||||
SND_SOC_DAPM_INPUT("MIC2P"),
|
||||
SND_SOC_DAPM_INPUT("MIC2N"),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("LOUT"),
|
||||
SND_SOC_DAPM_OUTPUT("ROUT"),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("LHPOUT"),
|
||||
SND_SOC_DAPM_OUTPUT("RHPOUT"),
|
||||
|
||||
SND_SOC_DAPM_INPUT("LLINEIN"),
|
||||
SND_SOC_DAPM_INPUT("RLINEIN"),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("SYSCLK"),
|
||||
};
|
||||
|
||||
/* Unconditional routes. */
|
||||
static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
|
||||
{ "Mic 1", NULL, "MIC1P" },
|
||||
{ "Mic Diff", NULL, "MIC1N" },
|
||||
{ "Mic 1", NULL, "Mic Diff" },
|
||||
{ "Mic 2", NULL, "MIC2P" },
|
||||
{ "Mic Diff", NULL, "MIC2N" },
|
||||
{ "Mic 2", NULL, "Mic Diff" },
|
||||
|
||||
{ "Line In", NULL, "LLINEIN" },
|
||||
{ "Line In", NULL, "RLINEIN" },
|
||||
|
||||
{ "Mic", "Stereo Capture Switch", "Mic 1" },
|
||||
{ "Mic", "Stereo Capture Switch", "Mic 2" },
|
||||
{ "Headphones Source", "Mic 1", "Mic" },
|
||||
{ "Headphones Source", "Mic 2", "Mic" },
|
||||
{ "Capture Source", "Mic 1", "Mic" },
|
||||
{ "Capture Source", "Mic 2", "Mic" },
|
||||
|
||||
{ "Headphones Source", "Mic 1", "Mic 1" },
|
||||
{ "Headphones Source", "Mic 2", "Mic 2" },
|
||||
{ "Headphones Source", "Line In", "Line In Bypass" },
|
||||
{ "Headphones Source", "PCM", "Headphones Playback" },
|
||||
{ "HP Out", NULL, "Headphones Source" },
|
||||
|
||||
{ "Capture Source", "Line In", "Line In" },
|
||||
{ "Capture Source", "Mic 1", "Mic 1" },
|
||||
{ "Capture Source", "Mic 2", "Mic 2" },
|
||||
{ "ADC", NULL, "Capture Source" },
|
||||
|
||||
{ "Line In Bypass", NULL, "Line In" },
|
||||
{ "Line Out Source", "Line In", "Line In Bypass" },
|
||||
{ "Line Out Source", "PCM", "PCM Playback" },
|
||||
|
||||
{ "LHPOUT", NULL, "HP Out"},
|
||||
{ "RHPOUT", NULL, "HP Out"},
|
||||
|
||||
{ "Line Out", NULL, "Line Out Source" },
|
||||
{ "Line Out Switch 2", NULL, "Line Out" },
|
||||
|
||||
{ "LOUT", NULL, "Line Out Switch 2"},
|
||||
{ "ROUT", NULL, "Line Out Switch 2"},
|
||||
|
||||
{ "PCM Playback", "Volume", "DAC" },
|
||||
{ "Headphones Playback", "Volume", "PCM Playback" },
|
||||
|
||||
{ "SYSCLK", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
|
||||
{
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
struct regmap *regmap = jz_codec->regmap;
|
||||
|
||||
/* Collect updates for later sending. */
|
||||
regcache_cache_only(regmap, true);
|
||||
|
||||
/* default HP output to PCM */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SEL_MASK, REG_CR_HP_SEL_MASK);
|
||||
|
||||
/* default line output to PCM */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_LO,
|
||||
REG_CR_LO_SEL_MASK, REG_CR_LO_SEL_MASK);
|
||||
|
||||
/* Disable stereo mic */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
|
||||
BIT(REG_CR_MIC_STEREO_OFFSET), 0);
|
||||
|
||||
/* Set mic 1 as default source for ADC */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
|
||||
REG_CR_ADC_IN_SEL_MASK, 0);
|
||||
|
||||
/* ADC/DAC: serial + i2s */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
|
||||
REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S,
|
||||
REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
|
||||
REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S,
|
||||
REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
|
||||
|
||||
/* The generated IRQ is a high level */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_ICR,
|
||||
REG_ICR_INT_FORM_MASK, 0);
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
|
||||
REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
|
||||
REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
|
||||
REG_IMR_GDO_MASK);
|
||||
|
||||
/* 12M oscillator */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CCR,
|
||||
REG_CCR_CRYSTAL_MASK, 0);
|
||||
|
||||
/* 0: 16ohm/220uF, 1: 10kohm/1uF */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_LOAD, 0);
|
||||
|
||||
/* disable automatic gain */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN, 0);
|
||||
|
||||
/* Disable DAC lrswap */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_DAC,
|
||||
REG_CR_DAC_LRSWAP, REG_CR_DAC_LRSWAP);
|
||||
|
||||
/* Independent L/R DAC gain control */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
|
||||
REG_GCR_DACL_RLGOD, 0);
|
||||
|
||||
/* Disable ADC lrswap */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
|
||||
REG_CR_ADC_LRSWAP, REG_CR_ADC_LRSWAP);
|
||||
|
||||
/* default to cap-less mode(0) */
|
||||
regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP,
|
||||
REG_CR_HP_SB_HPCM, 0);
|
||||
|
||||
/* Send collected updates. */
|
||||
regcache_cache_only(regmap, false);
|
||||
regcache_sync(regmap);
|
||||
|
||||
/* Reset all interrupt flags. */
|
||||
regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK);
|
||||
}
|
||||
|
||||
static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
|
||||
{
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
|
||||
clk_prepare_enable(jz_codec->clk);
|
||||
|
||||
jz4770_codec_codec_init_regs(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4770_codec_codec_remove(struct snd_soc_component *codec)
|
||||
{
|
||||
struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
|
||||
|
||||
clk_disable_unprepare(jz_codec->clk);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
|
||||
.probe = jz4770_codec_codec_probe,
|
||||
.remove = jz4770_codec_codec_remove,
|
||||
.set_bias_level = jz4770_codec_set_bias_level,
|
||||
.controls = jz4770_codec_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(jz4770_codec_snd_controls),
|
||||
.dapm_widgets = jz4770_codec_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets),
|
||||
.dapm_routes = jz4770_codec_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes),
|
||||
.suspend_bias_off = 1,
|
||||
.use_pmdown_time = 1,
|
||||
};
|
||||
|
||||
static const unsigned int jz4770_codec_sample_rates[] = {
|
||||
96000, 48000, 44100, 32000,
|
||||
24000, 22050, 16000, 12000,
|
||||
11025, 9600, 8000,
|
||||
};
|
||||
|
||||
static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
|
||||
unsigned int rate, bit_width;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
bit_width = 0;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
bit_width = 1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
bit_width = 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_3LE:
|
||||
bit_width = 3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
|
||||
if (jz4770_codec_sample_rates[rate] == params_rate(params))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
|
||||
return -EINVAL;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
|
||||
REG_AICR_DAC_ADWL_MASK,
|
||||
bit_width << REG_AICR_DAC_ADWL_OFFSET);
|
||||
regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
|
||||
REG_FCR_DAC_FREQ_MASK,
|
||||
rate << REG_FCR_DAC_FREQ_OFFSET);
|
||||
} else {
|
||||
regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
|
||||
REG_AICR_ADC_ADWL_MASK,
|
||||
bit_width << REG_AICR_ADC_ADWL_OFFSET);
|
||||
regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
|
||||
REG_FCR_ADC_FREQ_MASK,
|
||||
rate << REG_FCR_ADC_FREQ_OFFSET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
|
||||
.startup = jz4770_codec_startup,
|
||||
.shutdown = jz4770_codec_shutdown,
|
||||
.hw_params = jz4770_codec_hw_params,
|
||||
.trigger = jz4770_codec_pcm_trigger,
|
||||
.digital_mute = jz4770_codec_digital_mute,
|
||||
};
|
||||
|
||||
#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE)
|
||||
|
||||
static struct snd_soc_dai_driver jz4770_codec_dai = {
|
||||
.name = "jz4770-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = JZ_CODEC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = JZ_CODEC_FORMATS,
|
||||
},
|
||||
.ops = &jz4770_codec_dai_ops,
|
||||
};
|
||||
|
||||
static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
|
||||
}
|
||||
|
||||
static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case JZ4770_CODEC_REG_MISSING_REG1:
|
||||
case JZ4770_CODEC_REG_MISSING_REG2:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case JZ4770_CODEC_REG_SR:
|
||||
case JZ4770_CODEC_REG_MISSING_REG1:
|
||||
case JZ4770_CODEC_REG_MISSING_REG2:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int jz4770_codec_io_wait(struct jz_codec *codec)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
|
||||
!(reg & ICDC_RGADW_RGWR),
|
||||
1000, 10 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static int jz4770_codec_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct jz_codec *codec = context;
|
||||
unsigned int i;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = jz4770_codec_io_wait(codec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = readl(codec->base + ICDC_RGADW_OFFSET);
|
||||
tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
|
||||
| (reg << ICDC_RGADW_RGADDR_OFFSET);
|
||||
writel(tmp, codec->base + ICDC_RGADW_OFFSET);
|
||||
|
||||
/* wait 6+ cycles */
|
||||
for (i = 0; i < 6; i++)
|
||||
*val = readl(codec->base + ICDC_RGDATA_OFFSET) &
|
||||
ICDC_RGDATA_RGDOUT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4770_codec_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct jz_codec *codec = context;
|
||||
int ret;
|
||||
|
||||
ret = jz4770_codec_io_wait(codec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
|
||||
codec->base + ICDC_RGADW_OFFSET);
|
||||
|
||||
ret = jz4770_codec_io_wait(codec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 jz4770_codec_reg_defaults[] = {
|
||||
0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
|
||||
0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
|
||||
0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
|
||||
0x07, 0x44, 0x1F, 0x00
|
||||
};
|
||||
|
||||
static struct regmap_config jz4770_codec_regmap_config = {
|
||||
.reg_bits = 7,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = JZ4770_CODEC_REG_AGC5,
|
||||
.volatile_reg = jz4770_codec_volatile,
|
||||
.readable_reg = jz4770_codec_readable,
|
||||
.writeable_reg = jz4770_codec_writeable,
|
||||
|
||||
.reg_read = jz4770_codec_reg_read,
|
||||
.reg_write = jz4770_codec_reg_write,
|
||||
|
||||
.reg_defaults_raw = jz4770_codec_reg_defaults,
|
||||
.num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int jz4770_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz_codec *codec;
|
||||
int ret;
|
||||
|
||||
codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
|
||||
if (!codec)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->dev = dev;
|
||||
|
||||
codec->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(codec->base)) {
|
||||
ret = PTR_ERR(codec->base);
|
||||
dev_err(dev, "Failed to ioremap mmio memory: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
codec->regmap = devm_regmap_init(dev, NULL, codec,
|
||||
&jz4770_codec_regmap_config);
|
||||
if (IS_ERR(codec->regmap))
|
||||
return PTR_ERR(codec->regmap);
|
||||
|
||||
codec->clk = devm_clk_get(dev, "aic");
|
||||
if (IS_ERR(codec->clk))
|
||||
return PTR_ERR(codec->clk);
|
||||
|
||||
platform_set_drvdata(pdev, codec);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
|
||||
&jz4770_codec_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id jz4770_codec_of_matches[] = {
|
||||
{ .compatible = "ingenic,jz4770-codec", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
|
||||
|
||||
static struct platform_driver jz4770_codec_driver = {
|
||||
.probe = jz4770_codec_probe,
|
||||
.driver = {
|
||||
.name = "jz4770-codec",
|
||||
.of_match_table = of_match_ptr(jz4770_codec_of_matches),
|
||||
},
|
||||
};
|
||||
module_platform_driver(jz4770_codec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
|
||||
MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
|
||||
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -163,6 +163,48 @@ static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = {
|
||||
MADERA_IRQ_DSP7_BUS_ERR,
|
||||
};
|
||||
|
||||
int madera_clk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
|
||||
struct madera *madera = priv->madera;
|
||||
unsigned int val;
|
||||
int clk_idx;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(madera->regmap, w->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(madera->dev, "Failed to check clock source: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) {
|
||||
case MADERA_CLK_SRC_MCLK1:
|
||||
clk_idx = MADERA_MCLK1;
|
||||
break;
|
||||
case MADERA_CLK_SRC_MCLK2:
|
||||
clk_idx = MADERA_MCLK2;
|
||||
break;
|
||||
case MADERA_CLK_SRC_MCLK3:
|
||||
clk_idx = MADERA_MCLK3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
return clk_prepare_enable(madera->mclk[clk_idx].clk);
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
clk_disable_unprepare(madera->mclk[clk_idx].clk);
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(madera_clk_ev);
|
||||
|
||||
static void madera_spin_sysclk(struct madera_priv *priv)
|
||||
{
|
||||
struct madera *madera = priv->madera;
|
||||
@ -193,9 +235,16 @@ int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
madera_spin_sysclk(priv);
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
madera_spin_sysclk(priv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return madera_clk_ev(w, kcontrol, event);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(madera_sysclk_ev);
|
||||
|
||||
@ -526,6 +575,7 @@ int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
|
||||
usleep_range(2000, 3000); /* wait for wseq to complete */
|
||||
|
||||
/* change demux setting */
|
||||
ret = 0;
|
||||
if (madera->out_clamp[0])
|
||||
ret = regmap_update_bits(madera->regmap,
|
||||
MADERA_OUTPUT_ENABLES_1,
|
||||
@ -3816,11 +3866,75 @@ static bool madera_set_fll_phase_integrator(struct madera_fll *fll,
|
||||
return reg_change;
|
||||
}
|
||||
|
||||
static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int shift)
|
||||
{
|
||||
struct madera *madera = fll->madera;
|
||||
unsigned int src;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(madera->regmap, reg, &src);
|
||||
if (ret != 0) {
|
||||
madera_fll_err(fll, "Failed to read current source: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
src = (src & mask) >> shift;
|
||||
|
||||
switch (src) {
|
||||
case MADERA_FLL_SRC_MCLK1:
|
||||
clk = madera->mclk[MADERA_MCLK1].clk;
|
||||
break;
|
||||
case MADERA_FLL_SRC_MCLK2:
|
||||
clk = madera->mclk[MADERA_MCLK2].clk;
|
||||
break;
|
||||
case MADERA_FLL_SRC_MCLK3:
|
||||
clk = madera->mclk[MADERA_MCLK3].clk;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ena) {
|
||||
return clk_prepare_enable(clk);
|
||||
} else {
|
||||
clk_disable_unprepare(clk);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena)
|
||||
{
|
||||
return madera_set_fll_clks_reg(fll, ena,
|
||||
base + MADERA_FLL_CONTROL_6_OFFS,
|
||||
MADERA_FLL1_REFCLK_SRC_MASK,
|
||||
MADERA_FLL1_REFCLK_DIV_SHIFT);
|
||||
}
|
||||
|
||||
static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena)
|
||||
{
|
||||
return madera_set_fll_clks_reg(fll, ena,
|
||||
base + MADERA_FLLAO_CONTROL_6_OFFS,
|
||||
MADERA_FLL_AO_REFCLK_SRC_MASK,
|
||||
MADERA_FLL_AO_REFCLK_SRC_SHIFT);
|
||||
}
|
||||
|
||||
static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena)
|
||||
{
|
||||
return madera_set_fll_clks_reg(fll, ena,
|
||||
base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
CS47L92_FLL1_REFCLK_SRC_MASK,
|
||||
CS47L92_FLL1_REFCLK_SRC_SHIFT);
|
||||
}
|
||||
|
||||
static void madera_disable_fll(struct madera_fll *fll)
|
||||
{
|
||||
struct madera *madera = fll->madera;
|
||||
unsigned int sync_base;
|
||||
bool change;
|
||||
bool ref_change, sync_change;
|
||||
|
||||
switch (madera->type) {
|
||||
case CS47L35:
|
||||
@ -3838,18 +3952,23 @@ static void madera_disable_fll(struct madera_fll *fll)
|
||||
MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN);
|
||||
regmap_update_bits_check(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_ENA, 0, &change);
|
||||
regmap_update_bits(madera->regmap,
|
||||
sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
|
||||
MADERA_FLL1_SYNC_ENA, 0);
|
||||
MADERA_FLL1_ENA, 0, &ref_change);
|
||||
regmap_update_bits_check(madera->regmap,
|
||||
sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
|
||||
MADERA_FLL1_SYNC_ENA, 0, &sync_change);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_FREERUN, 0);
|
||||
|
||||
madera_wait_for_fll(fll, false);
|
||||
|
||||
if (change)
|
||||
if (sync_change)
|
||||
madera_set_fll_clks(fll, sync_base, false);
|
||||
|
||||
if (ref_change) {
|
||||
madera_set_fll_clks(fll, fll->base, false);
|
||||
pm_runtime_put_autosuspend(madera->dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int madera_enable_fll(struct madera_fll *fll)
|
||||
@ -3905,6 +4024,10 @@ static int madera_enable_fll(struct madera_fll *fll)
|
||||
regmap_update_bits(fll->madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_7_OFFS,
|
||||
MADERA_FLL1_GAIN_MASK, 0);
|
||||
|
||||
if (sync_enabled > 0)
|
||||
madera_set_fll_clks(fll, sync_base, false);
|
||||
madera_set_fll_clks(fll, fll->base, false);
|
||||
}
|
||||
|
||||
/* Apply SYNCCLK setting */
|
||||
@ -3983,11 +4106,15 @@ static int madera_enable_fll(struct madera_fll *fll)
|
||||
if (!already_enabled)
|
||||
pm_runtime_get_sync(madera->dev);
|
||||
|
||||
if (have_sync)
|
||||
if (have_sync) {
|
||||
madera_set_fll_clks(fll, sync_base, true);
|
||||
regmap_update_bits(madera->regmap,
|
||||
sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
|
||||
MADERA_FLL1_SYNC_ENA,
|
||||
MADERA_FLL1_SYNC_ENA);
|
||||
}
|
||||
|
||||
madera_set_fll_clks(fll, fll->base, true);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_ENA, MADERA_FLL1_ENA);
|
||||
@ -4159,6 +4286,9 @@ static int madera_enable_fll_ao(struct madera_fll *fll,
|
||||
fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
|
||||
MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
|
||||
|
||||
if (already_enabled)
|
||||
madera_set_fllao_clks(fll, fll->base, false);
|
||||
|
||||
for (i = 0; i < patch_size; i++) {
|
||||
val = patch[i].def;
|
||||
|
||||
@ -4172,6 +4302,8 @@ static int madera_enable_fll_ao(struct madera_fll *fll,
|
||||
regmap_write(madera->regmap, patch[i].reg, val);
|
||||
}
|
||||
|
||||
madera_set_fllao_clks(fll, fll->base, true);
|
||||
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
|
||||
MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA);
|
||||
@ -4215,8 +4347,10 @@ static int madera_disable_fll_ao(struct madera_fll *fll)
|
||||
fll->base + MADERA_FLLAO_CONTROL_2_OFFS,
|
||||
MADERA_FLL_AO_CTRL_UPD_MASK, 0);
|
||||
|
||||
if (change)
|
||||
if (change) {
|
||||
madera_set_fllao_clks(fll, fll->base, false);
|
||||
pm_runtime_put_autosuspend(madera->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4302,8 +4436,10 @@ static int madera_fllhj_disable(struct madera_fll *fll)
|
||||
fll->base + MADERA_FLL_CONTROL_2_OFFS,
|
||||
MADERA_FLL1_CTRL_UPD_MASK, 0);
|
||||
|
||||
if (change)
|
||||
if (change) {
|
||||
madera_set_fllhj_clks(fll, fll->base, false);
|
||||
pm_runtime_put_autosuspend(madera->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4475,6 +4611,9 @@ static int madera_fllhj_enable(struct madera_fll *fll)
|
||||
MADERA_FLL1_HOLD_MASK,
|
||||
MADERA_FLL1_HOLD_MASK);
|
||||
|
||||
if (already_enabled)
|
||||
madera_set_fllhj_clks(fll, fll->base, false);
|
||||
|
||||
/* Apply refclk */
|
||||
ret = madera_fllhj_apply(fll, fll->ref_freq);
|
||||
if (ret) {
|
||||
@ -4486,6 +4625,8 @@ static int madera_fllhj_enable(struct madera_fll *fll)
|
||||
CS47L92_FLL1_REFCLK_SRC_MASK,
|
||||
fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT);
|
||||
|
||||
madera_set_fllhj_clks(fll, fll->base, true);
|
||||
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_ENA_MASK,
|
||||
@ -4573,7 +4714,7 @@ EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk);
|
||||
*
|
||||
* @component: Device to configure
|
||||
* @output: Output number
|
||||
* @diff: True to set the output to differential mode
|
||||
* @differential: True to set the output to differential mode
|
||||
*
|
||||
* Some systems use external analogue switches to connect more
|
||||
* analogue devices to the CODEC than are supported by the device. In
|
||||
|
@ -383,6 +383,8 @@ int madera_eq_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
||||
int madera_clk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int madera_spk_ev(struct snd_soc_dapm_widget *w,
|
||||
|
@ -5,24 +5,149 @@
|
||||
* Copyright 2011-2012 Maxim Integrated Products
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/max98090.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/max98090.h>
|
||||
#include "max98090.h"
|
||||
|
||||
static void max98090_shdn_save_locked(struct max98090_priv *max98090)
|
||||
{
|
||||
int shdn = 0;
|
||||
|
||||
/* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
|
||||
regmap_read(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, &shdn);
|
||||
max98090->saved_shdn |= shdn;
|
||||
++max98090->saved_count;
|
||||
|
||||
if (shdn)
|
||||
regmap_write(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, 0x0);
|
||||
}
|
||||
|
||||
static void max98090_shdn_restore_locked(struct max98090_priv *max98090)
|
||||
{
|
||||
/* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
|
||||
if (--max98090->saved_count == 0) {
|
||||
if (max98090->saved_shdn) {
|
||||
regmap_write(max98090->regmap,
|
||||
M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_MASK);
|
||||
max98090->saved_shdn = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void max98090_shdn_save(struct max98090_priv *max98090)
|
||||
{
|
||||
mutex_lock(&max98090->component->card->dapm_mutex);
|
||||
max98090_shdn_save_locked(max98090);
|
||||
}
|
||||
|
||||
static void max98090_shdn_restore(struct max98090_priv *max98090)
|
||||
{
|
||||
max98090_shdn_restore_locked(max98090);
|
||||
mutex_unlock(&max98090->component->card->dapm_mutex);
|
||||
}
|
||||
|
||||
static int max98090_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct max98090_priv *max98090 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
ret = snd_soc_put_volsw(kcontrol, ucontrol);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98090_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct max98090_priv *max98090 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
ret = snd_soc_dapm_put_enum_double_locked(kcontrol, ucontrol);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98090_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct max98090_priv *max98090 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98090_bytes_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct max98090_priv *max98090 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
ret = snd_soc_bytes_put(kcontrol, ucontrol);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98090_dapm_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct max98090_priv *max98090 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
max98090_shdn_save_locked(max98090);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
max98090_shdn_restore_locked(max98090);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allows for sparsely populated register maps */
|
||||
static const struct reg_default max98090_reg[] = {
|
||||
{ 0x00, 0x00 }, /* 00 Software Reset */
|
||||
@ -506,10 +631,13 @@ static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum,
|
||||
max98090_pwr_perf_text);
|
||||
|
||||
static const struct snd_kcontrol_new max98090_snd_controls[] = {
|
||||
SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
|
||||
SOC_ENUM_EXT("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
|
||||
SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
|
||||
M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
|
||||
SOC_SINGLE_EXT("DMIC MIC Comp Filter Config",
|
||||
M98090_REG_DIGITAL_MIC_CONFIG,
|
||||
M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
|
||||
SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
|
||||
M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
|
||||
@ -564,24 +692,34 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = {
|
||||
M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
|
||||
max98090_av_tlv),
|
||||
|
||||
SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
|
||||
SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
|
||||
M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
|
||||
SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
|
||||
SOC_ENUM_EXT("ADC Oversampling Rate", max98090_osr128_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_SINGLE_EXT("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
|
||||
M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_ENUM_EXT("ADC High Performance Mode", max98090_adchp_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
|
||||
SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
|
||||
SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
|
||||
SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
|
||||
SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
|
||||
SOC_ENUM("Filter Mode", max98090_mode_enum),
|
||||
SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
|
||||
SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
|
||||
SOC_SINGLE_EXT("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("SDIN Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_ENUM_EXT("Filter Mode", max98090_mode_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_SINGLE_EXT("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
|
||||
M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
|
||||
SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
|
||||
@ -594,13 +732,17 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
|
||||
M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
|
||||
max98090_dv_tlv),
|
||||
SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
|
||||
SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
|
||||
SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
|
||||
SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
|
||||
SND_SOC_BYTES_E("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105,
|
||||
snd_soc_bytes_get, max98090_bytes_put),
|
||||
SOC_SINGLE_EXT("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE_EXT("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
|
||||
M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
|
||||
1),
|
||||
@ -608,25 +750,34 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = {
|
||||
M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
|
||||
max98090_dv_tlv),
|
||||
|
||||
SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
|
||||
M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
|
||||
SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
|
||||
SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
|
||||
SOC_SINGLE_EXT("ALC Enable", M98090_REG_DRC_TIMING,
|
||||
M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
SOC_ENUM_EXT("ALC Attack Time", max98090_drcatk_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_ENUM_EXT("ALC Release Time", max98090_drcrls_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
|
||||
M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
|
||||
max98090_alcmakeup_tlv),
|
||||
SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
|
||||
SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
|
||||
SOC_SINGLE_TLV("ALC Compression Threshold Volume",
|
||||
SOC_ENUM_EXT("ALC Compression Ratio", max98090_alccmp_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_ENUM_EXT("ALC Expansion Ratio", max98090_drcexp_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_SINGLE_EXT_TLV("ALC Compression Threshold Volume",
|
||||
M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
|
||||
M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
|
||||
SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
|
||||
M98090_DRCTHC_NUM - 1, 1,
|
||||
snd_soc_get_volsw, max98090_put_volsw, max98090_alccomp_tlv),
|
||||
SOC_SINGLE_EXT_TLV("ALC Expansion Threshold Volume",
|
||||
M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
|
||||
M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
|
||||
M98090_DRCTHE_NUM - 1, 1,
|
||||
snd_soc_get_volsw, max98090_put_volsw, max98090_drcexp_tlv),
|
||||
|
||||
SOC_ENUM("DAC HP Playback Performance Mode",
|
||||
max98090_dac_perfmode_enum),
|
||||
SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
|
||||
SOC_ENUM_EXT("DAC HP Playback Performance Mode",
|
||||
max98090_dac_perfmode_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_ENUM_EXT("DAC High Performance Mode", max98090_dachp_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
|
||||
SOC_SINGLE_TLV("Headphone Left Mixer Volume",
|
||||
M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
|
||||
@ -684,9 +835,12 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = {
|
||||
SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
|
||||
M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
|
||||
|
||||
SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
|
||||
SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
|
||||
SND_SOC_BYTES_E("Biquad Coefficients",
|
||||
M98090_REG_RECORD_BIQUAD_BASE, 15,
|
||||
snd_soc_bytes_get, max98090_bytes_put),
|
||||
SOC_SINGLE_EXT("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new max98091_snd_controls[] = {
|
||||
@ -695,10 +849,12 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
|
||||
M98090_DMIC34_ZEROPAD_SHIFT,
|
||||
M98090_DMIC34_ZEROPAD_NUM - 1, 0),
|
||||
|
||||
SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
|
||||
SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
SOC_ENUM_EXT("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum,
|
||||
snd_soc_get_enum_double, max98090_put_enum_double),
|
||||
SOC_SINGLE_EXT("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
|
||||
M98090_FLT_DMIC34HPF_SHIFT,
|
||||
M98090_FLT_DMIC34HPF_NUM - 1, 0),
|
||||
M98090_FLT_DMIC34HPF_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
|
||||
SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
|
||||
M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
|
||||
@ -716,8 +872,9 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
|
||||
|
||||
SND_SOC_BYTES("DMIC34 Biquad Coefficients",
|
||||
M98090_REG_DMIC34_BIQUAD_BASE, 15),
|
||||
SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
|
||||
SOC_SINGLE_EXT("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
|
||||
M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0,
|
||||
snd_soc_get_volsw, max98090_put_volsw),
|
||||
|
||||
SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
|
||||
M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
|
||||
@ -771,19 +928,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (event & SND_SOC_DAPM_POST_PMU)
|
||||
max98090->shdn_pending = true;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static const char *mic1_mux_text[] = { "IN12", "IN56" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
|
||||
@ -884,10 +1028,14 @@ static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum,
|
||||
lten_mux_text);
|
||||
|
||||
static const struct snd_kcontrol_new max98090_ltenl_mux =
|
||||
SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
|
||||
SOC_DAPM_ENUM_EXT("LTENL Mux", ltenl_mux_enum,
|
||||
snd_soc_dapm_get_enum_double,
|
||||
max98090_dapm_put_enum_double);
|
||||
|
||||
static const struct snd_kcontrol_new max98090_ltenr_mux =
|
||||
SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
|
||||
SOC_DAPM_ENUM_EXT("LTENR Mux", ltenr_mux_enum,
|
||||
snd_soc_dapm_get_enum_double,
|
||||
max98090_dapm_put_enum_double);
|
||||
|
||||
static const char *lben_mux_text[] = { "Normal", "Loopback" };
|
||||
|
||||
@ -902,10 +1050,14 @@ static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum,
|
||||
lben_mux_text);
|
||||
|
||||
static const struct snd_kcontrol_new max98090_lbenl_mux =
|
||||
SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
|
||||
SOC_DAPM_ENUM_EXT("LBENL Mux", lbenl_mux_enum,
|
||||
snd_soc_dapm_get_enum_double,
|
||||
max98090_dapm_put_enum_double);
|
||||
|
||||
static const struct snd_kcontrol_new max98090_lbenr_mux =
|
||||
SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
|
||||
SOC_DAPM_ENUM_EXT("LBENR Mux", lbenr_mux_enum,
|
||||
snd_soc_dapm_get_enum_double,
|
||||
max98090_dapm_put_enum_double);
|
||||
|
||||
static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
|
||||
|
||||
@ -1072,21 +1224,25 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("IN56"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
|
||||
M98090_MBEN_SHIFT, 0, NULL, 0),
|
||||
M98090_MBEN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDIEN_SHIFT, 0, NULL, 0),
|
||||
M98090_SDIEN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
|
||||
M98090_SDOEN_SHIFT, 0, NULL, 0),
|
||||
M98090_SDOEN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
|
||||
M98090_DIGMICL_SHIFT, 0, max98090_shdn_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
M98090_DIGMICL_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
|
||||
M98090_DIGMICR_SHIFT, 0, max98090_shdn_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
M98090_DIGMICR_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
|
||||
M98090_AHPF_SHIFT, 0, NULL, 0),
|
||||
M98090_AHPF_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
/*
|
||||
* Note: Sysclk and misc power supplies are taken care of by SHDN
|
||||
@ -1116,10 +1272,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
|
||||
&max98090_lineb_mixer_controls[0],
|
||||
ARRAY_SIZE(max98090_lineb_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
|
||||
M98090_LINEAEN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
|
||||
M98090_LINEBEN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_E("LINEA Input", M98090_REG_INPUT_ENABLE,
|
||||
M98090_LINEAEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("LINEB Input", M98090_REG_INPUT_ENABLE,
|
||||
M98090_LINEBEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&max98090_left_adc_mixer_controls[0],
|
||||
@ -1130,11 +1288,11 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
|
||||
ARRAY_SIZE(max98090_right_adc_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE,
|
||||
M98090_ADLEN_SHIFT, 0, max98090_shdn_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
M98090_ADLEN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE,
|
||||
M98090_ADREN_SHIFT, 0, max98090_shdn_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
M98090_ADREN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
|
||||
SND_SOC_NOPM, 0, 0),
|
||||
@ -1162,10 +1320,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_DALEN_SHIFT, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_DAREN_SHIFT, 0),
|
||||
SND_SOC_DAPM_DAC_E("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_DALEN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_DAC_E("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_DAREN_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&max98090_left_hp_mixer_controls[0],
|
||||
@ -1200,20 +1360,26 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
|
||||
&max98090_mixhprsel_mux),
|
||||
|
||||
SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_HPLEN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_HPREN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_E("HP Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_HPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("HP Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_HPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_SPLEN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_SPREN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_E("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_SPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_SPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_RCVLEN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_RCVREN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_E("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_RCVLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
|
||||
M98090_RCVREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPL"),
|
||||
SND_SOC_DAPM_OUTPUT("HPR"),
|
||||
@ -1228,9 +1394,11 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("DMIC4"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
|
||||
M98090_DIGMIC3_SHIFT, 0, NULL, 0),
|
||||
M98090_DIGMIC3_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
|
||||
M98090_DIGMIC4_SHIFT, 0, NULL, 0),
|
||||
M98090_DIGMIC4_SHIFT, 0, max98090_dapm_event,
|
||||
SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
|
||||
@ -1501,6 +1669,11 @@ static void max98090_configure_bclk(struct snd_soc_component *component)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Master mode: no need to save and restore SHDN for the following
|
||||
* sensitive registers.
|
||||
*/
|
||||
|
||||
/* Check for supported PCLK to LRCLK ratios */
|
||||
for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
|
||||
if ((pclk_rates[i] == max98090->sysclk) &&
|
||||
@ -1587,12 +1760,14 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* Set to slave mode PLL - MAS mode off */
|
||||
max98090_shdn_save(max98090);
|
||||
snd_soc_component_write(component,
|
||||
M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
|
||||
snd_soc_component_write(component,
|
||||
M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
|
||||
snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
|
||||
M98090_USE_M1_MASK, 0);
|
||||
max98090_shdn_restore(max98090);
|
||||
max98090->master = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
@ -1618,7 +1793,9 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
||||
dev_err(component->dev, "DAI clock mode unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
max98090_shdn_save(max98090);
|
||||
snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
regval = 0;
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
@ -1663,8 +1840,10 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
||||
if (max98090->tdm_slots > 1)
|
||||
regval ^= M98090_BCI_MASK;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
snd_soc_component_write(component,
|
||||
M98090_REG_INTERFACE_FORMAT, regval);
|
||||
max98090_shdn_restore(max98090);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1676,6 +1855,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
|
||||
struct max98090_cdata *cdata;
|
||||
|
||||
cdata = &max98090->dai[0];
|
||||
|
||||
if (slots < 0 || slots > 4)
|
||||
@ -1685,6 +1865,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
|
||||
max98090->tdm_width = slot_width;
|
||||
|
||||
if (max98090->tdm_slots > 1) {
|
||||
max98090_shdn_save(max98090);
|
||||
/* SLOTL SLOTR SLOTDLY */
|
||||
snd_soc_component_write(component, M98090_REG_TDM_FORMAT,
|
||||
0 << M98090_TDM_SLOTL_SHIFT |
|
||||
@ -1695,6 +1876,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
|
||||
snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL,
|
||||
M98090_TDM_MASK,
|
||||
M98090_TDM_MASK);
|
||||
max98090_shdn_restore(max98090);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1894,6 +2076,7 @@ static int max98090_configure_dmic(struct max98090_priv *max98090,
|
||||
dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
|
||||
dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
|
||||
M98090_MICCLK_MASK,
|
||||
micclk_index << M98090_MICCLK_SHIFT);
|
||||
@ -1902,6 +2085,7 @@ static int max98090_configure_dmic(struct max98090_priv *max98090,
|
||||
M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
|
||||
dmic_comp << M98090_DMIC_COMP_SHIFT |
|
||||
dmic_freq << M98090_DMIC_FREQ_SHIFT);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1938,8 +2122,10 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
max98090_shdn_save(max98090);
|
||||
snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
|
||||
M98090_WS_MASK, 0);
|
||||
max98090_shdn_restore(max98090);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -1950,6 +2136,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
cdata->rate = max98090->lrclk;
|
||||
|
||||
max98090_shdn_save(max98090);
|
||||
/* Update filter mode */
|
||||
if (max98090->lrclk < 24000)
|
||||
snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
|
||||
@ -1965,6 +2152,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
|
||||
M98090_DHF_MASK, M98090_DHF_MASK);
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
|
||||
max98090->lrclk);
|
||||
@ -1995,6 +2183,7 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
|
||||
* 0x02 (when master clk is 20MHz to 40MHz)..
|
||||
* 0x03 (when master clk is 40MHz to 60MHz)..
|
||||
*/
|
||||
max98090_shdn_save(max98090);
|
||||
if ((freq >= 10000000) && (freq <= 20000000)) {
|
||||
snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
|
||||
M98090_PSCLK_DIV1);
|
||||
@ -2009,8 +2198,10 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
|
||||
max98090->pclk = freq >> 2;
|
||||
} else {
|
||||
dev_err(component->dev, "Invalid master clock frequency\n");
|
||||
max98090_shdn_restore(max98090);
|
||||
return -EINVAL;
|
||||
}
|
||||
max98090_shdn_restore(max98090);
|
||||
|
||||
max98090->sysclk = freq;
|
||||
|
||||
@ -2122,10 +2313,12 @@ static void max98090_pll_work(struct max98090_priv *max98090)
|
||||
*/
|
||||
|
||||
/* Toggle shutdown OFF then ON */
|
||||
mutex_lock(&component->card->dapm_mutex);
|
||||
snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_MASK, 0);
|
||||
snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
|
||||
mutex_unlock(&component->card->dapm_mutex);
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
/* Give PLL time to lock */
|
||||
@ -2448,7 +2641,12 @@ static int max98090_probe(struct snd_soc_component *component)
|
||||
*/
|
||||
snd_soc_component_read32(component, M98090_REG_DEVICE_STATUS);
|
||||
|
||||
/* High Performance is default */
|
||||
/*
|
||||
* SHDN should be 0 at the point, no need to save/restore for the
|
||||
* following registers.
|
||||
*
|
||||
* High Performance is default
|
||||
*/
|
||||
snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
|
||||
M98090_DACHP_MASK,
|
||||
1 << M98090_DACHP_SHIFT);
|
||||
@ -2459,7 +2657,12 @@ static int max98090_probe(struct snd_soc_component *component)
|
||||
M98090_ADCHP_MASK,
|
||||
1 << M98090_ADCHP_SHIFT);
|
||||
|
||||
/* Turn on VCM bandgap reference */
|
||||
/*
|
||||
* SHDN should be 0 at the point, no need to save/restore for the
|
||||
* following registers.
|
||||
*
|
||||
* Turn on VCM bandgap reference
|
||||
*/
|
||||
snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
|
||||
M98090_VCM_MODE_MASK);
|
||||
|
||||
@ -2491,25 +2694,9 @@ static void max98090_remove(struct snd_soc_component *component)
|
||||
max98090->component = NULL;
|
||||
}
|
||||
|
||||
static void max98090_seq_notifier(struct snd_soc_component *component,
|
||||
enum snd_soc_dapm_type event, int subseq)
|
||||
{
|
||||
struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (max98090->shdn_pending) {
|
||||
snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_MASK, 0);
|
||||
msleep(40);
|
||||
snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
|
||||
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
|
||||
max98090->shdn_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_max98090 = {
|
||||
.probe = max98090_probe,
|
||||
.remove = max98090_remove,
|
||||
.seq_notifier = max98090_seq_notifier,
|
||||
.set_bias_level = max98090_set_bias_level,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
@ -2651,17 +2838,12 @@ static int max98090_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max98090_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops max98090_pm = {
|
||||
SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
|
||||
max98090_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(max98090_suspend, max98090_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id max98090_i2c_id[] = {
|
||||
|
@ -1539,7 +1539,8 @@ struct max98090_priv {
|
||||
unsigned int pa2en;
|
||||
unsigned int sidetone;
|
||||
bool master;
|
||||
bool shdn_pending;
|
||||
int saved_count;
|
||||
int saved_shdn;
|
||||
};
|
||||
|
||||
int max98090_mic_detect(struct snd_soc_component *component,
|
||||
|
@ -374,9 +374,8 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component
|
||||
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_component
|
||||
*component, int event,
|
||||
int reg, unsigned int cap_mode)
|
||||
static int pm8916_wcd_analog_enable_micbias(struct snd_soc_component *component,
|
||||
int event, unsigned int cap_mode)
|
||||
{
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
@ -389,74 +388,46 @@ static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_component
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_component
|
||||
*component, int event,
|
||||
int reg, u32 cap_mode)
|
||||
static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_component_update_bits(component, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0);
|
||||
snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
|
||||
MICB_1_EN_OPA_STG2_TAIL_CURR_MASK,
|
||||
MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA);
|
||||
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
pm8916_wcd_analog_micbias_enable(component);
|
||||
snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
|
||||
MICB_1_EN_BYP_CAP_MASK, cap_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_micbias_ext1(struct
|
||||
snd_soc_dapm_widget
|
||||
*w, struct snd_kcontrol
|
||||
*kcontrol, int event)
|
||||
static int pm8916_wcd_analog_enable_micbias1(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
|
||||
|
||||
return pm8916_wcd_analog_enable_micbias_ext(component, event, w->reg,
|
||||
wcd->micbias1_cap_mode);
|
||||
return pm8916_wcd_analog_enable_micbias(component, event,
|
||||
wcd->micbias1_cap_mode);
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_micbias_ext2(struct
|
||||
snd_soc_dapm_widget
|
||||
*w, struct snd_kcontrol
|
||||
*kcontrol, int event)
|
||||
static int pm8916_wcd_analog_enable_micbias2(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
|
||||
|
||||
return pm8916_wcd_analog_enable_micbias_ext(component, event, w->reg,
|
||||
wcd->micbias2_cap_mode);
|
||||
return pm8916_wcd_analog_enable_micbias(component, event,
|
||||
wcd->micbias2_cap_mode);
|
||||
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_micbias_int1(struct
|
||||
snd_soc_dapm_widget
|
||||
*w, struct snd_kcontrol
|
||||
*kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
|
||||
MICB_1_INT_TX1_INT_RBIAS_EN_MASK,
|
||||
MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg,
|
||||
wcd->micbias1_cap_mode);
|
||||
}
|
||||
|
||||
static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
|
||||
bool micbias2_enabled)
|
||||
{
|
||||
@ -564,9 +535,8 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
|
||||
MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
|
||||
MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
|
||||
snd_soc_component_update_bits(component, CDC_A_MICB_2_EN,
|
||||
CDC_A_MICB_2_PULL_DOWN_EN_MASK, 0);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
pm8916_mbhc_configure_bias(wcd, true);
|
||||
@ -576,8 +546,7 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
|
||||
break;
|
||||
}
|
||||
|
||||
return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg,
|
||||
wcd->micbias2_cap_mode);
|
||||
return pm8916_wcd_analog_enable_micbias_int(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
|
||||
@ -878,14 +847,16 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
|
||||
{"SPK PA", NULL, "SPK DAC"},
|
||||
{"SPK DAC", "Switch", "PDM_RX3"},
|
||||
|
||||
{"MIC BIAS Internal1", NULL, "INT_LDO_H"},
|
||||
{"MIC BIAS Internal2", NULL, "INT_LDO_H"},
|
||||
{"MIC BIAS External1", NULL, "INT_LDO_H"},
|
||||
{"MIC BIAS External2", NULL, "INT_LDO_H"},
|
||||
{"MIC BIAS Internal1", NULL, "vdd-micbias"},
|
||||
{"MIC BIAS Internal2", NULL, "vdd-micbias"},
|
||||
{"MIC BIAS External1", NULL, "vdd-micbias"},
|
||||
{"MIC BIAS External2", NULL, "vdd-micbias"},
|
||||
{"MIC_BIAS1", NULL, "INT_LDO_H"},
|
||||
{"MIC_BIAS2", NULL, "INT_LDO_H"},
|
||||
{"MIC_BIAS1", NULL, "vdd-micbias"},
|
||||
{"MIC_BIAS2", NULL, "vdd-micbias"},
|
||||
|
||||
{"MIC BIAS External1", NULL, "MIC_BIAS1"},
|
||||
{"MIC BIAS Internal1", NULL, "MIC_BIAS1"},
|
||||
{"MIC BIAS External2", NULL, "MIC_BIAS2"},
|
||||
{"MIC BIAS Internal2", NULL, "MIC_BIAS2"},
|
||||
{"MIC BIAS Internal3", NULL, "MIC_BIAS1"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
|
||||
@ -937,21 +908,26 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0),
|
||||
|
||||
/* TX */
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias_int1,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_2_EN, 7, 0,
|
||||
SND_SOC_DAPM_SUPPLY("MIC_BIAS1", CDC_A_MICB_1_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias1,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("MIC_BIAS2", CDC_A_MICB_2_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias2,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS External1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS External2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_INT_RBIAS, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias_int,
|
||||
SND_SOC_DAPM_PRE_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_1_INT_RBIAS, 4, 0,
|
||||
pm8916_wcd_analog_enable_micbias_int2,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS External1", CDC_A_MICB_1_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias_ext1,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_micbias_ext2,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_INT_RBIAS, 1, 0,
|
||||
pm8916_wcd_analog_enable_micbias_int,
|
||||
SND_SOC_DAPM_PRE_PMU),
|
||||
|
||||
SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0,
|
||||
pm8916_wcd_analog_enable_adc,
|
||||
|
509
sound/soc/codecs/mt6660.c
Normal file
509
sound/soc/codecs/mt6660.c
Normal file
@ -0,0 +1,509 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 //
|
||||
|
||||
// Copyright (c) 2019 MediaTek Inc.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "mt6660.h"
|
||||
|
||||
struct reg_size_table {
|
||||
u32 addr;
|
||||
u8 size;
|
||||
};
|
||||
|
||||
static const struct reg_size_table mt6660_reg_size_table[] = {
|
||||
{ MT6660_REG_HPF1_COEF, 4 },
|
||||
{ MT6660_REG_HPF2_COEF, 4 },
|
||||
{ MT6660_REG_TDM_CFG3, 2 },
|
||||
{ MT6660_REG_RESV17, 2 },
|
||||
{ MT6660_REG_RESV23, 2 },
|
||||
{ MT6660_REG_SIGMAX, 2 },
|
||||
{ MT6660_REG_DEVID, 2 },
|
||||
{ MT6660_REG_HCLIP_CTRL, 2 },
|
||||
{ MT6660_REG_DA_GAIN, 2 },
|
||||
};
|
||||
|
||||
static int mt6660_get_reg_size(uint32_t addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) {
|
||||
if (mt6660_reg_size_table[i].addr == addr)
|
||||
return mt6660_reg_size_table[i].size;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct mt6660_chip *chip = context;
|
||||
int size = mt6660_get_reg_size(reg);
|
||||
u8 reg_data[4];
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
reg_data[size - i - 1] = (val >> (8 * i)) & 0xff;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct mt6660_chip *chip = context;
|
||||
int size = mt6660_get_reg_size(reg);
|
||||
int i, ret;
|
||||
u8 data[4];
|
||||
u32 reg_data = 0;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
for (i = 0; i < size; i++) {
|
||||
reg_data <<= 8;
|
||||
reg_data |= data[i];
|
||||
}
|
||||
*val = reg_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config mt6660_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_write = mt6660_reg_write,
|
||||
.reg_read = mt6660_reg_read,
|
||||
};
|
||||
|
||||
static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (event == SND_SOC_DAPM_POST_PMU)
|
||||
usleep_range(1000, 1100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6660_codec_classd_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
dev_dbg(component->dev,
|
||||
"%s: before classd turn on\n", __func__);
|
||||
/* config to adaptive mode */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_BST_CTRL, 0x03, 0x03);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "config mode adaptive fail\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
/* voltage sensing enable */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_RESV7, 0x04, 0x04);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev,
|
||||
"enable voltage sensing fail\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(component->dev, "Amp on\n");
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
dev_dbg(component->dev, "Amp off\n");
|
||||
/* voltage sensing disable */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_RESV7, 0x04, 0x00);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev,
|
||||
"disable voltage sensing fail\n");
|
||||
return ret;
|
||||
}
|
||||
/* pop-noise improvement 1 */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_RESV10, 0x10, 0x10);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev,
|
||||
"pop-noise improvement 1 fail\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
dev_dbg(component->dev,
|
||||
"%s: after classd turn off\n", __func__);
|
||||
/* pop-noise improvement 2 */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_RESV10, 0x10, 0x00);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev,
|
||||
"pop-noise improvement 2 fail\n");
|
||||
return ret;
|
||||
}
|
||||
/* config to off mode */
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
MT6660_REG_BST_CTRL, 0x03, 0x00);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "config mode off fail\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1,
|
||||
0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0,
|
||||
NULL, 0, mt6660_codec_classd_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_OUTPUT("OUTP"),
|
||||
SND_SOC_DAPM_OUTPUT("OUTN"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = {
|
||||
{ "DAC", NULL, "aif_playback" },
|
||||
{ "PGA", NULL, "DAC" },
|
||||
{ "ClassD", NULL, "PGA" },
|
||||
{ "OUTP", NULL, "ClassD" },
|
||||
{ "OUTN", NULL, "ClassD" },
|
||||
{ "VI ADC", NULL, "ClassD" },
|
||||
{ "aif_capture", NULL, "VI ADC" },
|
||||
};
|
||||
|
||||
static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct mt6660_chip *chip = (struct mt6660_chip *)
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0);
|
||||
|
||||
static const struct snd_kcontrol_new mt6660_component_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255,
|
||||
1, vol_ctl_tlv),
|
||||
SOC_SINGLE("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0),
|
||||
SOC_SINGLE("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0),
|
||||
SOC_SINGLE("Boost Mode", MT6660_REG_BST_CTRL, 0, 3, 0),
|
||||
SOC_SINGLE("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0),
|
||||
SOC_SINGLE("DC Protect Switch", MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0),
|
||||
SOC_SINGLE("Data Output Left Channel Selection",
|
||||
MT6660_REG_DATAO_SEL, 3, 7, 0),
|
||||
SOC_SINGLE("Data Output Right Channel Selection",
|
||||
MT6660_REG_DATAO_SEL, 0, 7, 0),
|
||||
SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0,
|
||||
snd_soc_get_volsw, NULL),
|
||||
SOC_SINGLE_EXT("Chip Rev", MT6660_REG_DEVID, 8, 15, 0,
|
||||
mt6660_component_get_volsw, NULL),
|
||||
};
|
||||
|
||||
static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off)
|
||||
{
|
||||
return regmap_write_bits(chip->regmap, MT6660_REG_SYSTEM_CTRL,
|
||||
0x01, on_off ? 0x00 : 0x01);
|
||||
}
|
||||
|
||||
static int mt6660_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
|
||||
|
||||
dev_dbg(component->dev, "%s\n", __func__);
|
||||
snd_soc_component_init_regmap(component, chip->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt6660_component_remove(struct snd_soc_component *component)
|
||||
{
|
||||
dev_dbg(component->dev, "%s\n", __func__);
|
||||
snd_soc_component_exit_regmap(component);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver mt6660_component_driver = {
|
||||
.probe = mt6660_component_probe,
|
||||
.remove = mt6660_component_remove,
|
||||
|
||||
.controls = mt6660_component_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(mt6660_component_snd_controls),
|
||||
.dapm_widgets = mt6660_component_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets),
|
||||
.dapm_routes = mt6660_component_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
|
||||
|
||||
.idle_bias_on = false, /* idle_bias_off = true */
|
||||
};
|
||||
|
||||
static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
|
||||
{
|
||||
int word_len = params_physical_width(hw_params);
|
||||
int aud_bit = params_width(hw_params);
|
||||
u16 reg_data = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dai->dev, "%s: ++\n", __func__);
|
||||
dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params));
|
||||
dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params));
|
||||
dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit);
|
||||
if (word_len > 32 || word_len < 16) {
|
||||
dev_err(dai->dev, "not supported word length\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
switch (aud_bit) {
|
||||
case 16:
|
||||
reg_data = 3;
|
||||
break;
|
||||
case 18:
|
||||
reg_data = 2;
|
||||
break;
|
||||
case 20:
|
||||
reg_data = 1;
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
reg_data = 0;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "config aud bit fail\n");
|
||||
return ret;
|
||||
}
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "config word len fail\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dai->dev, "%s: --\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mt6660_component_aif_ops = {
|
||||
.hw_params = mt6660_component_aif_hw_params,
|
||||
};
|
||||
|
||||
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
|
||||
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_U16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_U24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE | \
|
||||
SNDRV_PCM_FMTBIT_U32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mt6660_codec_dai = {
|
||||
.name = "mt6660-aif",
|
||||
.playback = {
|
||||
.stream_name = "aif_playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = STUB_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "aif_capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = STUB_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
},
|
||||
/* dai properties */
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_channels = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
/* dai operations */
|
||||
.ops = &mt6660_component_aif_ops,
|
||||
};
|
||||
|
||||
static int _mt6660_chip_id_check(struct mt6660_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val &= 0x0ff0;
|
||||
if (val != 0x00e0 && val != 0x01e0) {
|
||||
dev_err(chip->dev, "%s id(%x) not match\n", __func__, val);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _mt6660_chip_sw_reset(struct mt6660_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* turn on main pll first, then trigger reset */
|
||||
ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(30);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _mt6660_read_chip_revision(struct mt6660_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "get chip revision fail\n");
|
||||
return ret;
|
||||
}
|
||||
chip->chip_rev = val&0xff;
|
||||
dev_info(chip->dev, "%s chip_rev = %x\n", __func__, chip->chip_rev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6660_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mt6660_chip *chip = NULL;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
chip->i2c = client;
|
||||
chip->dev = &client->dev;
|
||||
mutex_init(&chip->io_lock);
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
chip->regmap = devm_regmap_init(&client->dev,
|
||||
NULL, chip, &mt6660_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
ret = PTR_ERR(chip->regmap);
|
||||
dev_err(&client->dev, "failed to initialise regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* chip reset first */
|
||||
ret = _mt6660_chip_sw_reset(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "chip reset fail\n");
|
||||
goto probe_fail;
|
||||
}
|
||||
/* chip power on */
|
||||
ret = _mt6660_chip_power_on(chip, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "chip power on 2 fail\n");
|
||||
goto probe_fail;
|
||||
}
|
||||
/* chip devid check */
|
||||
ret = _mt6660_chip_id_check(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "chip id check fail\n");
|
||||
goto probe_fail;
|
||||
}
|
||||
/* chip revision get */
|
||||
ret = _mt6660_read_chip_revision(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "read chip revision fail\n");
|
||||
goto probe_fail;
|
||||
}
|
||||
pm_runtime_set_active(chip->dev);
|
||||
pm_runtime_enable(chip->dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(chip->dev,
|
||||
&mt6660_component_driver,
|
||||
&mt6660_codec_dai, 1);
|
||||
return ret;
|
||||
probe_fail:
|
||||
_mt6660_chip_power_on(chip, 0);
|
||||
mutex_destroy(&chip->io_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6660_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt6660_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
pm_runtime_disable(chip->dev);
|
||||
pm_runtime_set_suspended(chip->dev);
|
||||
mutex_destroy(&chip->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mt6660_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "enter low power mode\n");
|
||||
return regmap_update_bits(chip->regmap,
|
||||
MT6660_REG_SYSTEM_CTRL, 0x01, 0x01);
|
||||
}
|
||||
|
||||
static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mt6660_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "exit low power mode\n");
|
||||
return regmap_update_bits(chip->regmap,
|
||||
MT6660_REG_SYSTEM_CTRL, 0x01, 0x00);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mt6660_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend,
|
||||
mt6660_i2c_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id __maybe_unused mt6660_of_id[] = {
|
||||
{ .compatible = "mediatek,mt6660",},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6660_of_id);
|
||||
|
||||
static const struct i2c_device_id mt6660_i2c_id[] = {
|
||||
{"mt6660", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id);
|
||||
|
||||
static struct i2c_driver mt6660_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "mt6660",
|
||||
.of_match_table = of_match_ptr(mt6660_of_id),
|
||||
.pm = &mt6660_dev_pm_ops,
|
||||
},
|
||||
.probe = mt6660_i2c_probe,
|
||||
.remove = mt6660_i2c_remove,
|
||||
.id_table = mt6660_i2c_id,
|
||||
};
|
||||
module_i2c_driver(mt6660_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
|
||||
MODULE_DESCRIPTION("MT6660 SPKAMP Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("1.0.7_G");
|
77
sound/soc/codecs/mt6660.h
Normal file
77
sound/soc/codecs/mt6660.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2019 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#ifndef __SND_SOC_MT6660_H
|
||||
#define __SND_SOC_MT6660_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct mt6660_platform_data {
|
||||
u8 init_setting_num;
|
||||
u32 *init_setting_addr;
|
||||
u32 *init_setting_mask;
|
||||
u32 *init_setting_val;
|
||||
};
|
||||
|
||||
struct mt6660_chip {
|
||||
struct i2c_client *i2c;
|
||||
struct device *dev;
|
||||
struct platform_device *param_dev;
|
||||
struct mt6660_platform_data plat_data;
|
||||
struct mutex io_lock;
|
||||
struct regmap *regmap;
|
||||
u16 chip_rev;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#define MT6660_REG_DEVID (0x00)
|
||||
#define MT6660_REG_SYSTEM_CTRL (0x03)
|
||||
#define MT6660_REG_IRQ_STATUS1 (0x05)
|
||||
#define MT6660_REG_ADDA_CLOCK (0x07)
|
||||
#define MT6660_REG_SERIAL_CFG1 (0x10)
|
||||
#define MT6660_REG_DATAO_SEL (0x12)
|
||||
#define MT6660_REG_TDM_CFG3 (0x15)
|
||||
#define MT6660_REG_HPF_CTRL (0x18)
|
||||
#define MT6660_REG_HPF1_COEF (0x1A)
|
||||
#define MT6660_REG_HPF2_COEF (0x1B)
|
||||
#define MT6660_REG_PATH_BYPASS (0x1E)
|
||||
#define MT6660_REG_WDT_CTRL (0x20)
|
||||
#define MT6660_REG_HCLIP_CTRL (0x24)
|
||||
#define MT6660_REG_VOL_CTRL (0x29)
|
||||
#define MT6660_REG_SPS_CTRL (0x30)
|
||||
#define MT6660_REG_SIGMAX (0x33)
|
||||
#define MT6660_REG_CALI_T0 (0x3F)
|
||||
#define MT6660_REG_BST_CTRL (0x40)
|
||||
#define MT6660_REG_PROTECTION_CFG (0x46)
|
||||
#define MT6660_REG_DA_GAIN (0x4c)
|
||||
#define MT6660_REG_AUDIO_IN2_SEL (0x50)
|
||||
#define MT6660_REG_SIG_GAIN (0x51)
|
||||
#define MT6660_REG_PLL_CFG1 (0x60)
|
||||
#define MT6660_REG_DRE_CTRL (0x68)
|
||||
#define MT6660_REG_DRE_THDMODE (0x69)
|
||||
#define MT6660_REG_DRE_CORASE (0x6B)
|
||||
#define MT6660_REG_PWM_CTRL (0x70)
|
||||
#define MT6660_REG_DC_PROTECT_CTRL (0x74)
|
||||
#define MT6660_REG_ADC_USB_MODE (0x7c)
|
||||
#define MT6660_REG_INTERNAL_CFG (0x88)
|
||||
#define MT6660_REG_RESV0 (0x98)
|
||||
#define MT6660_REG_RESV1 (0x99)
|
||||
#define MT6660_REG_RESV2 (0x9A)
|
||||
#define MT6660_REG_RESV3 (0x9B)
|
||||
#define MT6660_REG_RESV6 (0xA2)
|
||||
#define MT6660_REG_RESV7 (0xA3)
|
||||
#define MT6660_REG_RESV10 (0xB0)
|
||||
#define MT6660_REG_RESV11 (0xB1)
|
||||
#define MT6660_REG_RESV16 (0xB6)
|
||||
#define MT6660_REG_RESV17 (0xB7)
|
||||
#define MT6660_REG_RESV19 (0xB9)
|
||||
#define MT6660_REG_RESV21 (0xBB)
|
||||
#define MT6660_REG_RESV23 (0xBD)
|
||||
#define MT6660_REG_RESV31 (0xD3)
|
||||
#define MT6660_REG_RESV40 (0xE0)
|
||||
|
||||
#endif /* __SND_SOC_MT6660_H */
|
@ -40,7 +40,6 @@ static const struct reg_sequence init_list[] = {
|
||||
|
||||
{ RT1011_ADC_SET_5, 0x0a20 },
|
||||
{ RT1011_DAC_SET_2, 0xa032 },
|
||||
{ RT1011_ADC_SET_1, 0x2925 },
|
||||
|
||||
{ RT1011_SPK_PRO_DC_DET_1, 0xb00c },
|
||||
{ RT1011_SPK_PRO_DC_DET_2, 0xcccc },
|
||||
@ -2186,7 +2185,6 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
/* ADC/DAC setting */
|
||||
regmap_write(rt1011->regmap, RT1011_ADC_SET_5, 0x0a20);
|
||||
regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xe232);
|
||||
regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925);
|
||||
regmap_write(rt1011->regmap, RT1011_ADC_SET_4, 0xc000);
|
||||
|
||||
/* DC detection */
|
||||
@ -2235,8 +2233,18 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
dc_offset |= (value & 0xffff);
|
||||
dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
|
||||
|
||||
/* check the package info. */
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_MATCH_DONE, &value);
|
||||
if (value & 0x4)
|
||||
rt1011->pack_id = 1;
|
||||
|
||||
if (cali_flag) {
|
||||
|
||||
if (rt1011->pack_id)
|
||||
regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x292c);
|
||||
else
|
||||
regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925);
|
||||
|
||||
/* Class D on */
|
||||
regmap_write(rt1011->regmap, RT1011_CLASS_D_POS, 0x010e);
|
||||
regmap_write(rt1011->regmap,
|
||||
@ -2361,6 +2369,11 @@ static void rt1011_calibration_work(struct work_struct *work)
|
||||
|
||||
rt1011_r0_load(rt1011);
|
||||
}
|
||||
|
||||
if (rt1011->pack_id)
|
||||
snd_soc_component_write(component, RT1011_ADC_SET_1, 0x292c);
|
||||
else
|
||||
snd_soc_component_write(component, RT1011_ADC_SET_1, 0x2925);
|
||||
}
|
||||
|
||||
static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
|
||||
|
@ -692,6 +692,7 @@ struct rt1011_priv {
|
||||
unsigned int r0_reg, cali_done;
|
||||
unsigned int r0_calib, temperature_calib;
|
||||
int recv_spk_mode;
|
||||
unsigned int pack_id; /* 0: WLCSP; 1: QFN */
|
||||
};
|
||||
|
||||
#endif /* end of _RT1011_H_ */
|
||||
|
993
sound/soc/codecs/rt1015.c
Normal file
993
sound/soc/codecs/rt1015.c
Normal file
@ -0,0 +1,993 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// rt1015.c -- RT1015 ALSA SoC audio amplifier driver
|
||||
//
|
||||
// Copyright 2019 Realtek Semiconductor Corp.
|
||||
//
|
||||
// Author: Jack Yu <jack.yu@realtek.com>
|
||||
//
|
||||
//
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "rl6231.h"
|
||||
#include "rt1015.h"
|
||||
|
||||
static const struct reg_default rt1015_reg[] = {
|
||||
{ 0x0000, 0x0000 },
|
||||
{ 0x0004, 0xa000 },
|
||||
{ 0x0006, 0x0003 },
|
||||
{ 0x000a, 0x0802 },
|
||||
{ 0x000c, 0x0020 },
|
||||
{ 0x000e, 0x0000 },
|
||||
{ 0x0010, 0x0000 },
|
||||
{ 0x0012, 0x0000 },
|
||||
{ 0x0020, 0x8000 },
|
||||
{ 0x0022, 0x471b },
|
||||
{ 0x006a, 0x0000 },
|
||||
{ 0x006c, 0x4020 },
|
||||
{ 0x0076, 0x0000 },
|
||||
{ 0x0078, 0x0000 },
|
||||
{ 0x007a, 0x0000 },
|
||||
{ 0x007c, 0x10ec },
|
||||
{ 0x007d, 0x1015 },
|
||||
{ 0x00f0, 0x5000 },
|
||||
{ 0x00f2, 0x0774 },
|
||||
{ 0x00f3, 0x8400 },
|
||||
{ 0x00f4, 0x0000 },
|
||||
{ 0x0100, 0x0028 },
|
||||
{ 0x0102, 0xff02 },
|
||||
{ 0x0104, 0x8232 },
|
||||
{ 0x0106, 0x200c },
|
||||
{ 0x010c, 0x002f },
|
||||
{ 0x010e, 0xc000 },
|
||||
{ 0x0111, 0x0200 },
|
||||
{ 0x0112, 0x0400 },
|
||||
{ 0x0114, 0x0022 },
|
||||
{ 0x0116, 0x0000 },
|
||||
{ 0x0118, 0x0000 },
|
||||
{ 0x011a, 0x0123 },
|
||||
{ 0x011c, 0x4567 },
|
||||
{ 0x0300, 0xdddd },
|
||||
{ 0x0302, 0x0000 },
|
||||
{ 0x0311, 0x9330 },
|
||||
{ 0x0313, 0x0000 },
|
||||
{ 0x0314, 0x0000 },
|
||||
{ 0x031a, 0x00a0 },
|
||||
{ 0x031c, 0x001f },
|
||||
{ 0x031d, 0xffff },
|
||||
{ 0x031e, 0x0000 },
|
||||
{ 0x031f, 0x0000 },
|
||||
{ 0x0321, 0x0000 },
|
||||
{ 0x0322, 0x0000 },
|
||||
{ 0x0328, 0x0000 },
|
||||
{ 0x0329, 0x0000 },
|
||||
{ 0x032a, 0x0000 },
|
||||
{ 0x032b, 0x0000 },
|
||||
{ 0x032c, 0x0000 },
|
||||
{ 0x032d, 0x0000 },
|
||||
{ 0x032e, 0x030e },
|
||||
{ 0x0330, 0x0080 },
|
||||
{ 0x0332, 0x0034 },
|
||||
{ 0x0334, 0x0000 },
|
||||
{ 0x0336, 0x0000 },
|
||||
{ 0x0506, 0x04ff },
|
||||
{ 0x0508, 0x0030 },
|
||||
{ 0x050a, 0x0018 },
|
||||
{ 0x0519, 0x307f },
|
||||
{ 0x051a, 0xffff },
|
||||
{ 0x051b, 0x4000 },
|
||||
{ 0x051d, 0x0000 },
|
||||
{ 0x051f, 0x0000 },
|
||||
{ 0x0536, 0x1000 },
|
||||
{ 0x0538, 0x0000 },
|
||||
{ 0x053a, 0x0000 },
|
||||
{ 0x053c, 0x0000 },
|
||||
{ 0x053d, 0x0000 },
|
||||
{ 0x053e, 0x0000 },
|
||||
{ 0x053f, 0x0000 },
|
||||
{ 0x0540, 0x0000 },
|
||||
{ 0x0541, 0x0000 },
|
||||
{ 0x0542, 0x0000 },
|
||||
{ 0x0543, 0x0000 },
|
||||
{ 0x0544, 0x0000 },
|
||||
{ 0x0568, 0x0000 },
|
||||
{ 0x056a, 0x0000 },
|
||||
{ 0x1000, 0x0000 },
|
||||
{ 0x1002, 0x6505 },
|
||||
{ 0x1006, 0x5515 },
|
||||
{ 0x1007, 0x003f },
|
||||
{ 0x1009, 0x770f },
|
||||
{ 0x100a, 0x01ff },
|
||||
{ 0x100c, 0x0000 },
|
||||
{ 0x100d, 0x0003 },
|
||||
{ 0x1010, 0xa433 },
|
||||
{ 0x1020, 0x0000 },
|
||||
{ 0x1200, 0x3d02 },
|
||||
{ 0x1202, 0x0813 },
|
||||
{ 0x1204, 0x0211 },
|
||||
{ 0x1206, 0x0000 },
|
||||
{ 0x1208, 0x0000 },
|
||||
{ 0x120a, 0x0000 },
|
||||
{ 0x120c, 0x0000 },
|
||||
{ 0x120e, 0x0000 },
|
||||
{ 0x1210, 0x0000 },
|
||||
{ 0x1212, 0x0000 },
|
||||
{ 0x1300, 0x0701 },
|
||||
{ 0x1302, 0x12f9 },
|
||||
{ 0x1304, 0x3405 },
|
||||
{ 0x1305, 0x0844 },
|
||||
{ 0x1306, 0x1611 },
|
||||
{ 0x1308, 0x555e },
|
||||
{ 0x130a, 0x0000 },
|
||||
{ 0x130c, 0x2400},
|
||||
{ 0x130e, 0x7700 },
|
||||
{ 0x130f, 0x0000 },
|
||||
{ 0x1310, 0x0000 },
|
||||
{ 0x1312, 0x0000 },
|
||||
{ 0x1314, 0x0000 },
|
||||
{ 0x1316, 0x0000 },
|
||||
{ 0x1318, 0x0000 },
|
||||
{ 0x131a, 0x0000 },
|
||||
{ 0x1322, 0x0029 },
|
||||
{ 0x1323, 0x4a52 },
|
||||
{ 0x1324, 0x002c },
|
||||
{ 0x1325, 0x0b02 },
|
||||
{ 0x1326, 0x002d },
|
||||
{ 0x1327, 0x6b5a },
|
||||
{ 0x1328, 0x002e },
|
||||
{ 0x1329, 0xcbb2 },
|
||||
{ 0x132a, 0x0030 },
|
||||
{ 0x132b, 0x2c0b },
|
||||
{ 0x1330, 0x0031 },
|
||||
{ 0x1331, 0x8c63 },
|
||||
{ 0x1332, 0x0032 },
|
||||
{ 0x1333, 0xecbb },
|
||||
{ 0x1334, 0x0034 },
|
||||
{ 0x1335, 0x4d13 },
|
||||
{ 0x1336, 0x0037 },
|
||||
{ 0x1337, 0x0dc3 },
|
||||
{ 0x1338, 0x003d },
|
||||
{ 0x1339, 0xef7b },
|
||||
{ 0x133a, 0x0044 },
|
||||
{ 0x133b, 0xd134 },
|
||||
{ 0x133c, 0x0047 },
|
||||
{ 0x133d, 0x91e4 },
|
||||
{ 0x133e, 0x004d },
|
||||
{ 0x133f, 0xc370 },
|
||||
{ 0x1340, 0x0053 },
|
||||
{ 0x1341, 0xf4fd },
|
||||
{ 0x1342, 0x0060 },
|
||||
{ 0x1343, 0x5816 },
|
||||
{ 0x1344, 0x006c },
|
||||
{ 0x1345, 0xbb2e },
|
||||
{ 0x1346, 0x0072 },
|
||||
{ 0x1347, 0xecbb },
|
||||
{ 0x1348, 0x0076 },
|
||||
{ 0x1349, 0x5d97 },
|
||||
};
|
||||
|
||||
static bool rt1015_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case RT1015_RESET:
|
||||
case RT1015_CLK_DET:
|
||||
case RT1015_SIL_DET:
|
||||
case RT1015_VER_ID:
|
||||
case RT1015_VENDOR_ID:
|
||||
case RT1015_DEVICE_ID:
|
||||
case RT1015_PRO_ALT:
|
||||
case RT1015_DAC3:
|
||||
case RT1015_VBAT_TEST_OUT1:
|
||||
case RT1015_VBAT_TEST_OUT2:
|
||||
case RT1015_VBAT_PROT_ATT:
|
||||
case RT1015_VBAT_DET_CODE:
|
||||
case RT1015_SMART_BST_CTRL1:
|
||||
case RT1015_SPK_DC_DETECT1:
|
||||
case RT1015_SPK_DC_DETECT4:
|
||||
case RT1015_SPK_DC_DETECT5:
|
||||
case RT1015_DC_CALIB_CLSD1:
|
||||
case RT1015_DC_CALIB_CLSD5:
|
||||
case RT1015_DC_CALIB_CLSD6:
|
||||
case RT1015_DC_CALIB_CLSD7:
|
||||
case RT1015_DC_CALIB_CLSD8:
|
||||
case RT1015_S_BST_TIMING_INTER1:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt1015_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case RT1015_RESET:
|
||||
case RT1015_CLK2:
|
||||
case RT1015_CLK3:
|
||||
case RT1015_PLL1:
|
||||
case RT1015_PLL2:
|
||||
case RT1015_CLK_DET:
|
||||
case RT1015_SIL_DET:
|
||||
case RT1015_CUSTOMER_ID:
|
||||
case RT1015_PCODE_FWVER:
|
||||
case RT1015_VER_ID:
|
||||
case RT1015_VENDOR_ID:
|
||||
case RT1015_DEVICE_ID:
|
||||
case RT1015_PAD_DRV1:
|
||||
case RT1015_PAD_DRV2:
|
||||
case RT1015_GAT_BOOST:
|
||||
case RT1015_PRO_ALT:
|
||||
case RT1015_MAN_I2C:
|
||||
case RT1015_DAC1:
|
||||
case RT1015_DAC2:
|
||||
case RT1015_DAC3:
|
||||
case RT1015_ADC1:
|
||||
case RT1015_ADC2:
|
||||
case RT1015_TDM_MASTER:
|
||||
case RT1015_TDM_TCON:
|
||||
case RT1015_TDM1_1:
|
||||
case RT1015_TDM1_2:
|
||||
case RT1015_TDM1_3:
|
||||
case RT1015_TDM1_4:
|
||||
case RT1015_TDM1_5:
|
||||
case RT1015_MIXER1:
|
||||
case RT1015_MIXER2:
|
||||
case RT1015_ANA_PROTECT1:
|
||||
case RT1015_ANA_CTRL_SEQ1:
|
||||
case RT1015_ANA_CTRL_SEQ2:
|
||||
case RT1015_VBAT_DET_DEB:
|
||||
case RT1015_VBAT_VOLT_DET1:
|
||||
case RT1015_VBAT_VOLT_DET2:
|
||||
case RT1015_VBAT_TEST_OUT1:
|
||||
case RT1015_VBAT_TEST_OUT2:
|
||||
case RT1015_VBAT_PROT_ATT:
|
||||
case RT1015_VBAT_DET_CODE:
|
||||
case RT1015_PWR1:
|
||||
case RT1015_PWR4:
|
||||
case RT1015_PWR5:
|
||||
case RT1015_PWR6:
|
||||
case RT1015_PWR7:
|
||||
case RT1015_PWR8:
|
||||
case RT1015_PWR9:
|
||||
case RT1015_CLASSD_SEQ:
|
||||
case RT1015_SMART_BST_CTRL1:
|
||||
case RT1015_SMART_BST_CTRL2:
|
||||
case RT1015_ANA_CTRL1:
|
||||
case RT1015_ANA_CTRL2:
|
||||
case RT1015_SPK_VOL:
|
||||
case RT1015_SHORT_DETTOP1:
|
||||
case RT1015_SHORT_DETTOP2:
|
||||
case RT1015_SPK_DC_DETECT1:
|
||||
case RT1015_SPK_DC_DETECT2:
|
||||
case RT1015_SPK_DC_DETECT3:
|
||||
case RT1015_SPK_DC_DETECT4:
|
||||
case RT1015_SPK_DC_DETECT5:
|
||||
case RT1015_BAT_RPO_STEP1:
|
||||
case RT1015_BAT_RPO_STEP2:
|
||||
case RT1015_BAT_RPO_STEP3:
|
||||
case RT1015_BAT_RPO_STEP4:
|
||||
case RT1015_BAT_RPO_STEP5:
|
||||
case RT1015_BAT_RPO_STEP6:
|
||||
case RT1015_BAT_RPO_STEP7:
|
||||
case RT1015_BAT_RPO_STEP8:
|
||||
case RT1015_BAT_RPO_STEP9:
|
||||
case RT1015_BAT_RPO_STEP10:
|
||||
case RT1015_BAT_RPO_STEP11:
|
||||
case RT1015_BAT_RPO_STEP12:
|
||||
case RT1015_SPREAD_SPEC1:
|
||||
case RT1015_SPREAD_SPEC2:
|
||||
case RT1015_PAD_STATUS:
|
||||
case RT1015_PADS_PULLING_CTRL1:
|
||||
case RT1015_PADS_DRIVING:
|
||||
case RT1015_SYS_RST1:
|
||||
case RT1015_SYS_RST2:
|
||||
case RT1015_SYS_GATING1:
|
||||
case RT1015_TEST_MODE1:
|
||||
case RT1015_TEST_MODE2:
|
||||
case RT1015_TIMING_CTRL1:
|
||||
case RT1015_PLL_INT:
|
||||
case RT1015_TEST_OUT1:
|
||||
case RT1015_DC_CALIB_CLSD1:
|
||||
case RT1015_DC_CALIB_CLSD2:
|
||||
case RT1015_DC_CALIB_CLSD3:
|
||||
case RT1015_DC_CALIB_CLSD4:
|
||||
case RT1015_DC_CALIB_CLSD5:
|
||||
case RT1015_DC_CALIB_CLSD6:
|
||||
case RT1015_DC_CALIB_CLSD7:
|
||||
case RT1015_DC_CALIB_CLSD8:
|
||||
case RT1015_DC_CALIB_CLSD9:
|
||||
case RT1015_DC_CALIB_CLSD10:
|
||||
case RT1015_CLSD_INTERNAL1:
|
||||
case RT1015_CLSD_INTERNAL2:
|
||||
case RT1015_CLSD_INTERNAL3:
|
||||
case RT1015_CLSD_INTERNAL4:
|
||||
case RT1015_CLSD_INTERNAL5:
|
||||
case RT1015_CLSD_INTERNAL6:
|
||||
case RT1015_CLSD_INTERNAL7:
|
||||
case RT1015_CLSD_INTERNAL8:
|
||||
case RT1015_CLSD_INTERNAL9:
|
||||
case RT1015_CLSD_OCP_CTRL:
|
||||
case RT1015_VREF_LV:
|
||||
case RT1015_MBIAS1:
|
||||
case RT1015_MBIAS2:
|
||||
case RT1015_MBIAS3:
|
||||
case RT1015_MBIAS4:
|
||||
case RT1015_VREF_LV1:
|
||||
case RT1015_S_BST_TIMING_INTER1:
|
||||
case RT1015_S_BST_TIMING_INTER2:
|
||||
case RT1015_S_BST_TIMING_INTER3:
|
||||
case RT1015_S_BST_TIMING_INTER4:
|
||||
case RT1015_S_BST_TIMING_INTER5:
|
||||
case RT1015_S_BST_TIMING_INTER6:
|
||||
case RT1015_S_BST_TIMING_INTER7:
|
||||
case RT1015_S_BST_TIMING_INTER8:
|
||||
case RT1015_S_BST_TIMING_INTER9:
|
||||
case RT1015_S_BST_TIMING_INTER10:
|
||||
case RT1015_S_BST_TIMING_INTER11:
|
||||
case RT1015_S_BST_TIMING_INTER12:
|
||||
case RT1015_S_BST_TIMING_INTER13:
|
||||
case RT1015_S_BST_TIMING_INTER14:
|
||||
case RT1015_S_BST_TIMING_INTER15:
|
||||
case RT1015_S_BST_TIMING_INTER16:
|
||||
case RT1015_S_BST_TIMING_INTER17:
|
||||
case RT1015_S_BST_TIMING_INTER18:
|
||||
case RT1015_S_BST_TIMING_INTER19:
|
||||
case RT1015_S_BST_TIMING_INTER20:
|
||||
case RT1015_S_BST_TIMING_INTER21:
|
||||
case RT1015_S_BST_TIMING_INTER22:
|
||||
case RT1015_S_BST_TIMING_INTER23:
|
||||
case RT1015_S_BST_TIMING_INTER24:
|
||||
case RT1015_S_BST_TIMING_INTER25:
|
||||
case RT1015_S_BST_TIMING_INTER26:
|
||||
case RT1015_S_BST_TIMING_INTER27:
|
||||
case RT1015_S_BST_TIMING_INTER28:
|
||||
case RT1015_S_BST_TIMING_INTER29:
|
||||
case RT1015_S_BST_TIMING_INTER30:
|
||||
case RT1015_S_BST_TIMING_INTER31:
|
||||
case RT1015_S_BST_TIMING_INTER32:
|
||||
case RT1015_S_BST_TIMING_INTER33:
|
||||
case RT1015_S_BST_TIMING_INTER34:
|
||||
case RT1015_S_BST_TIMING_INTER35:
|
||||
case RT1015_S_BST_TIMING_INTER36:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0);
|
||||
|
||||
static const char * const rt1015_din_source_select[] = {
|
||||
"Left",
|
||||
"Right",
|
||||
"Left + Right average",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt1015_mono_lr_sel, RT1015_PAD_DRV2, 4,
|
||||
rt1015_din_source_select);
|
||||
|
||||
static const char * const rt1015_boost_mode[] = {
|
||||
"Bypass", "Adaptive", "Fixed Adaptive"
|
||||
};
|
||||
|
||||
static const SOC_ENUM_SINGLE_DECL(rt1015_boost_mode_enum, 0, 0,
|
||||
rt1015_boost_mode);
|
||||
|
||||
static int rt1015_boost_mode_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct rt1015_priv *rt1015 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
ucontrol->value.integer.value[0] = rt1015->boost_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct rt1015_priv *rt1015 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
rt1015->boost_mode = ucontrol->value.integer.value[0];
|
||||
|
||||
switch (rt1015->boost_mode) {
|
||||
case BYPASS:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
|
||||
RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
|
||||
RT1015_ABST_REG_MODE | RT1015_ABST_FIX_TGT_DIS |
|
||||
RT1015_BYPASS_SWRREG_BYPASS);
|
||||
break;
|
||||
case ADAPTIVE:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
|
||||
RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
|
||||
RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_DIS |
|
||||
RT1015_BYPASS_SWRREG_PASS);
|
||||
break;
|
||||
case FIXED_ADAPTIVE:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
|
||||
RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
|
||||
RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_EN |
|
||||
RT1015_BYPASS_SWRREG_PASS);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Unknown boost control.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5518_bypass_boost_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct rt1015_priv *rt1015 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
ucontrol->value.integer.value[0] = rt1015->bypass_boost;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5518_bypass_boost_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct rt1015_priv *rt1015 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!rt1015->dac_is_used) {
|
||||
rt1015->bypass_boost = ucontrol->value.integer.value[0];
|
||||
if (rt1015->bypass_boost == 1) {
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR4, 0x00b2);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_CLSD_INTERNAL8, 0x2008);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_CLSD_INTERNAL9, 0x0140);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_GAT_BOOST, 0x00fe);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR_STATE_CTRL, 0x000d);
|
||||
msleep(500);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR_STATE_CTRL, 0x000e);
|
||||
}
|
||||
} else
|
||||
dev_err(component->dev, "DAC is being used!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new rt1015_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT,
|
||||
127, 0, dac_vol_tlv),
|
||||
SOC_DOUBLE("DAC Playback Switch", RT1015_DAC3,
|
||||
RT1015_DA_MUTE_SFT, RT1015_DVOL_MUTE_FLAG_SFT, 1, 1),
|
||||
SOC_ENUM_EXT("Boost Mode", rt1015_boost_mode_enum,
|
||||
rt1015_boost_mode_get, rt1015_boost_mode_put),
|
||||
SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel),
|
||||
SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0,
|
||||
rt5518_bypass_boost_get, rt5518_bypass_boost_put),
|
||||
};
|
||||
|
||||
static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(source->dapm);
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (rt1015->sysclk_src == RT1015_SCLK_S_PLL)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r1015_dac_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
rt1015->dac_is_used = 1;
|
||||
if (rt1015->bypass_boost == 0) {
|
||||
snd_soc_component_write(component,
|
||||
RT1015_SYS_RST1, 0x05f7);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_GAT_BOOST, 0xacfe);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR9, 0xaa00);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_GAT_BOOST, 0xecfe);
|
||||
} else {
|
||||
snd_soc_component_write(component,
|
||||
RT1015_SYS_RST1, 0x05f7);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR_STATE_CTRL, 0x026e);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
if (rt1015->bypass_boost == 0) {
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR9, 0xa800);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_SYS_RST1, 0x05f5);
|
||||
} else {
|
||||
snd_soc_component_write(component,
|
||||
RT1015_PWR_STATE_CTRL, 0x0268);
|
||||
snd_soc_component_write(component,
|
||||
RT1015_SYS_RST1, 0x05f5);
|
||||
}
|
||||
rt1015->dac_is_used = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("LDO2", RT1015_PWR1, RT1015_PWR_LDO2_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("INT RC CLK", RT1015_PWR1, RT1015_PWR_INTCLK_BIT,
|
||||
0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ISENSE", RT1015_PWR1, RT1015_PWR_ISENSE_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VSENSE", RT1015_PWR1, RT1015_PWR_VSENSE_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("BG1 BG2", RT1015_PWR1, RT1015_PWR_BG_1_2_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MBIAS BG", RT1015_PWR1, RT1015_PWR_MBIAS_BG_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VBAT", RT1015_PWR1, RT1015_PWR_VBAT_BIT, 0, NULL,
|
||||
0),
|
||||
SND_SOC_DAPM_SUPPLY("MBIAS", RT1015_PWR1, RT1015_PWR_MBIAS_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADCV", RT1015_PWR1, RT1015_PWR_ADCV_BIT, 0, NULL,
|
||||
0),
|
||||
SND_SOC_DAPM_SUPPLY("MIXERV", RT1015_PWR1, RT1015_PWR_MIXERV_BIT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("SUMV", RT1015_PWR1, RT1015_PWR_SUMV_BIT, 0, NULL,
|
||||
0),
|
||||
SND_SOC_DAPM_SUPPLY("VREFLV", RT1015_PWR1, RT1015_PWR_VREFLV_BIT, 0,
|
||||
NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC_E("DAC", NULL, RT1015_PWR1, RT1015_PWR_DAC_BIT, 0,
|
||||
r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("SPO"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt1015_dapm_routes[] = {
|
||||
{ "DAC", NULL, "AIFRX" },
|
||||
{ "DAC", NULL, "LDO2" },
|
||||
{ "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll},
|
||||
{ "DAC", NULL, "INT RC CLK" },
|
||||
{ "DAC", NULL, "ISENSE" },
|
||||
{ "DAC", NULL, "VSENSE" },
|
||||
{ "DAC", NULL, "BG1 BG2" },
|
||||
{ "DAC", NULL, "MBIAS BG" },
|
||||
{ "DAC", NULL, "VBAT" },
|
||||
{ "DAC", NULL, "MBIAS" },
|
||||
{ "DAC", NULL, "ADCV" },
|
||||
{ "DAC", NULL, "MIXERV" },
|
||||
{ "DAC", NULL, "SUMV" },
|
||||
{ "DAC", NULL, "VREFLV" },
|
||||
{ "SPO", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static int rt1015_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
int pre_div, bclk_ms, frame_size;
|
||||
unsigned int val_len = 0;
|
||||
|
||||
rt1015->lrck = params_rate(params);
|
||||
pre_div = rl6231_get_clk_info(rt1015->sysclk, rt1015->lrck);
|
||||
if (pre_div < 0) {
|
||||
dev_err(component->dev, "Unsupported clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
frame_size = snd_soc_params_to_frame_size(params);
|
||||
if (frame_size < 0) {
|
||||
dev_err(component->dev, "Unsupported frame size: %d\n",
|
||||
frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bclk_ms = frame_size > 32;
|
||||
rt1015->bclk = rt1015->lrck * (32 << bclk_ms);
|
||||
|
||||
dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
|
||||
bclk_ms, pre_div, dai->id);
|
||||
|
||||
dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
|
||||
rt1015->lrck, pre_div, dai->id);
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
break;
|
||||
case 20:
|
||||
val_len = RT1015_I2S_DL_20;
|
||||
break;
|
||||
case 24:
|
||||
val_len = RT1015_I2S_DL_24;
|
||||
break;
|
||||
case 8:
|
||||
val_len = RT1015_I2S_DL_8;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
|
||||
RT1015_I2S_DL_MASK, val_len);
|
||||
snd_soc_component_update_bits(component, RT1015_CLK2,
|
||||
RT1015_FS_PD_MASK, pre_div);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
unsigned int reg_val = 0, reg_val2 = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
reg_val |= RT1015_TCON_TDM_MS_M;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
reg_val |= RT1015_TCON_TDM_MS_S;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
reg_val2 |= RT1015_TDM_INV_BCLK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
reg_val |= RT1015_I2S_M_DF_LEFT;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
reg_val |= RT1015_I2S_M_DF_PCM_A;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
reg_val |= RT1015_I2S_M_DF_PCM_B;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
|
||||
RT1015_TCON_TDM_MS_MASK | RT1015_I2S_M_DF_MASK,
|
||||
reg_val);
|
||||
snd_soc_component_update_bits(component, RT1015_TDM1_1,
|
||||
RT1015_TDM_INV_BCLK_MASK, reg_val2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_set_component_sysclk(struct snd_soc_component *component,
|
||||
int clk_id, int source, unsigned int freq, int dir)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int reg_val = 0;
|
||||
|
||||
if (freq == rt1015->sysclk && clk_id == rt1015->sysclk_src)
|
||||
return 0;
|
||||
|
||||
switch (clk_id) {
|
||||
case RT1015_SCLK_S_MCLK:
|
||||
reg_val |= RT1015_CLK_SYS_PRE_SEL_MCLK;
|
||||
break;
|
||||
|
||||
case RT1015_SCLK_S_PLL:
|
||||
reg_val |= RT1015_CLK_SYS_PRE_SEL_PLL;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rt1015->sysclk = freq;
|
||||
rt1015->sysclk_src = clk_id;
|
||||
|
||||
dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
|
||||
freq, clk_id);
|
||||
|
||||
snd_soc_component_update_bits(component, RT1015_CLK2,
|
||||
RT1015_CLK_SYS_PRE_SEL_MASK, reg_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_set_component_pll(struct snd_soc_component *component,
|
||||
int pll_id, int source, unsigned int freq_in,
|
||||
unsigned int freq_out)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
struct rl6231_pll_code pll_code;
|
||||
int ret;
|
||||
|
||||
if (!freq_in || !freq_out) {
|
||||
dev_dbg(component->dev, "PLL disabled\n");
|
||||
|
||||
rt1015->pll_in = 0;
|
||||
rt1015->pll_out = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (source == rt1015->pll_src && freq_in == rt1015->pll_in &&
|
||||
freq_out == rt1015->pll_out)
|
||||
return 0;
|
||||
|
||||
switch (source) {
|
||||
case RT1015_PLL_S_MCLK:
|
||||
snd_soc_component_update_bits(component, RT1015_CLK2,
|
||||
RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_PLL_SRC2);
|
||||
break;
|
||||
|
||||
case RT1015_PLL_S_BCLK:
|
||||
snd_soc_component_update_bits(component, RT1015_CLK2,
|
||||
RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_BCLK);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(component->dev, "Unknown PLL Source %d\n", source);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
|
||||
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
|
||||
pll_code.n_code, pll_code.k_code);
|
||||
|
||||
snd_soc_component_write(component, RT1015_PLL1,
|
||||
(pll_code.m_bp ? 0 : pll_code.m_code) << RT1015_PLL_M_SFT |
|
||||
pll_code.m_bp << RT1015_PLL_M_BP_SFT | pll_code.n_code);
|
||||
snd_soc_component_write(component, RT1015_PLL2,
|
||||
pll_code.k_code);
|
||||
|
||||
rt1015->pll_in = freq_in;
|
||||
rt1015->pll_out = freq_out;
|
||||
rt1015->pll_src = source;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt1015_priv *rt1015 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
rt1015->component = component;
|
||||
snd_soc_component_write(component, RT1015_BAT_RPO_STEP1, 0x061c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rt1015_remove(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regmap_write(rt1015->regmap, RT1015_RESET, 0);
|
||||
}
|
||||
|
||||
#define RT1015_STEREO_RATES SNDRV_PCM_RATE_8000_192000
|
||||
#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
|
||||
|
||||
struct snd_soc_dai_ops rt1015_aif_dai_ops = {
|
||||
.hw_params = rt1015_hw_params,
|
||||
.set_fmt = rt1015_set_dai_fmt,
|
||||
};
|
||||
|
||||
struct snd_soc_dai_driver rt1015_dai[] = {
|
||||
{
|
||||
.name = "rt1015-aif",
|
||||
.id = 0,
|
||||
.playback = {
|
||||
.stream_name = "AIF Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 4,
|
||||
.rates = RT1015_STEREO_RATES,
|
||||
.formats = RT1015_FORMATS,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rt1015_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regcache_cache_only(rt1015->regmap, true);
|
||||
regcache_mark_dirty(rt1015->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1015_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regcache_cache_only(rt1015->regmap, false);
|
||||
regcache_sync(rt1015->regmap);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define rt1015_suspend NULL
|
||||
#define rt1015_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_rt1015 = {
|
||||
.probe = rt1015_probe,
|
||||
.remove = rt1015_remove,
|
||||
.suspend = rt1015_suspend,
|
||||
.resume = rt1015_resume,
|
||||
.controls = rt1015_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(rt1015_snd_controls),
|
||||
.dapm_widgets = rt1015_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rt1015_dapm_widgets),
|
||||
.dapm_routes = rt1015_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(rt1015_dapm_routes),
|
||||
.set_sysclk = rt1015_set_component_sysclk,
|
||||
.set_pll = rt1015_set_component_pll,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt1015_regmap = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.max_register = RT1015_S_BST_TIMING_INTER36,
|
||||
.volatile_reg = rt1015_volatile_register,
|
||||
.readable_reg = rt1015_readable_register,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = rt1015_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt1015_reg),
|
||||
};
|
||||
|
||||
static const struct i2c_device_id rt1015_i2c_id[] = {
|
||||
{ "rt1015", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id rt1015_of_match[] = {
|
||||
{ .compatible = "realtek,rt1015", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rt1015_of_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static struct acpi_device_id rt1015_acpi_match[] = {
|
||||
{"10EC1015", 0,},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match);
|
||||
#endif
|
||||
|
||||
static int rt1015_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct rt1015_priv *rt1015;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
rt1015 = devm_kzalloc(&i2c->dev, sizeof(struct rt1015_priv),
|
||||
GFP_KERNEL);
|
||||
if (rt1015 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, rt1015);
|
||||
|
||||
rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap);
|
||||
if (IS_ERR(rt1015->regmap)) {
|
||||
ret = PTR_ERR(rt1015->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val);
|
||||
if ((val != RT1015_DEVICE_ID_VAL) && (val != RT1015_DEVICE_ID_VAL2)) {
|
||||
dev_err(&i2c->dev,
|
||||
"Device with ID register %x is not rt1015\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return devm_snd_soc_register_component(&i2c->dev,
|
||||
&soc_component_dev_rt1015,
|
||||
rt1015_dai, ARRAY_SIZE(rt1015_dai));
|
||||
}
|
||||
|
||||
static void rt1015_i2c_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct rt1015_priv *rt1015 = i2c_get_clientdata(client);
|
||||
|
||||
regmap_write(rt1015->regmap, RT1015_RESET, 0);
|
||||
}
|
||||
|
||||
static struct i2c_driver rt1015_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rt1015",
|
||||
.of_match_table = of_match_ptr(rt1015_of_match),
|
||||
.acpi_match_table = ACPI_PTR(rt1015_acpi_match),
|
||||
},
|
||||
.probe = rt1015_i2c_probe,
|
||||
.shutdown = rt1015_i2c_shutdown,
|
||||
.id_table = rt1015_i2c_id,
|
||||
};
|
||||
module_i2c_driver(rt1015_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT1015 driver");
|
||||
MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
375
sound/soc/codecs/rt1015.h
Normal file
375
sound/soc/codecs/rt1015.h
Normal file
@ -0,0 +1,375 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// rt1015.h -- RT1015 ALSA SoC audio amplifier driver
|
||||
//
|
||||
// Copyright 2019 Realtek Semiconductor Corp.
|
||||
// Author: Jack Yu <jack.yu@realtek.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
|
||||
#ifndef __RT1015_H__
|
||||
#define __RT1015_H__
|
||||
|
||||
#define RT1015_DEVICE_ID_VAL 0x1011
|
||||
#define RT1015_DEVICE_ID_VAL2 0x1015
|
||||
|
||||
#define RT1015_RESET 0x0000
|
||||
#define RT1015_CLK2 0x0004
|
||||
#define RT1015_CLK3 0x0006
|
||||
#define RT1015_PLL1 0x000a
|
||||
#define RT1015_PLL2 0x000c
|
||||
#define RT1015_CLK_DET 0x0020
|
||||
#define RT1015_SIL_DET 0x0022
|
||||
#define RT1015_CUSTOMER_ID 0x0076
|
||||
#define RT1015_PCODE_FWVER 0x0078
|
||||
#define RT1015_VER_ID 0x007a
|
||||
#define RT1015_VENDOR_ID 0x007c
|
||||
#define RT1015_DEVICE_ID 0x007d
|
||||
#define RT1015_PAD_DRV1 0x00f0
|
||||
#define RT1015_PAD_DRV2 0x00f2
|
||||
#define RT1015_GAT_BOOST 0x00f3
|
||||
#define RT1015_PRO_ALT 0x00f4
|
||||
#define RT1015_MAN_I2C 0x0100
|
||||
#define RT1015_DAC1 0x0102
|
||||
#define RT1015_DAC2 0x0104
|
||||
#define RT1015_DAC3 0x0106
|
||||
#define RT1015_ADC1 0x010c
|
||||
#define RT1015_ADC2 0x010e
|
||||
#define RT1015_TDM_MASTER 0x0111
|
||||
#define RT1015_TDM_TCON 0x0112
|
||||
#define RT1015_TDM1_1 0x0114
|
||||
#define RT1015_TDM1_2 0x0116
|
||||
#define RT1015_TDM1_3 0x0118
|
||||
#define RT1015_TDM1_4 0x011a
|
||||
#define RT1015_TDM1_5 0x011c
|
||||
#define RT1015_MIXER1 0x0300
|
||||
#define RT1015_MIXER2 0x0302
|
||||
#define RT1015_ANA_PROTECT1 0x0311
|
||||
#define RT1015_ANA_CTRL_SEQ1 0x0313
|
||||
#define RT1015_ANA_CTRL_SEQ2 0x0314
|
||||
#define RT1015_VBAT_DET_DEB 0x031a
|
||||
#define RT1015_VBAT_VOLT_DET1 0x031c
|
||||
#define RT1015_VBAT_VOLT_DET2 0x031d
|
||||
#define RT1015_VBAT_TEST_OUT1 0x031e
|
||||
#define RT1015_VBAT_TEST_OUT2 0x031f
|
||||
#define RT1015_VBAT_PROT_ATT 0x0320
|
||||
#define RT1015_VBAT_DET_CODE 0x0321
|
||||
#define RT1015_PWR1 0x0322
|
||||
#define RT1015_PWR4 0x0328
|
||||
#define RT1015_PWR5 0x0329
|
||||
#define RT1015_PWR6 0x032a
|
||||
#define RT1015_PWR7 0x032b
|
||||
#define RT1015_PWR8 0x032c
|
||||
#define RT1015_PWR9 0x032d
|
||||
#define RT1015_CLASSD_SEQ 0x032e
|
||||
#define RT1015_SMART_BST_CTRL1 0x0330
|
||||
#define RT1015_SMART_BST_CTRL2 0x0332
|
||||
#define RT1015_ANA_CTRL1 0x0334
|
||||
#define RT1015_ANA_CTRL2 0x0336
|
||||
#define RT1015_PWR_STATE_CTRL 0x0338
|
||||
#define RT1015_SPK_VOL 0x0506
|
||||
#define RT1015_SHORT_DETTOP1 0x0508
|
||||
#define RT1015_SHORT_DETTOP2 0x050a
|
||||
#define RT1015_SPK_DC_DETECT1 0x0519
|
||||
#define RT1015_SPK_DC_DETECT2 0x051a
|
||||
#define RT1015_SPK_DC_DETECT3 0x051b
|
||||
#define RT1015_SPK_DC_DETECT4 0x051d
|
||||
#define RT1015_SPK_DC_DETECT5 0x051f
|
||||
#define RT1015_BAT_RPO_STEP1 0x0536
|
||||
#define RT1015_BAT_RPO_STEP2 0x0538
|
||||
#define RT1015_BAT_RPO_STEP3 0x053a
|
||||
#define RT1015_BAT_RPO_STEP4 0x053c
|
||||
#define RT1015_BAT_RPO_STEP5 0x053d
|
||||
#define RT1015_BAT_RPO_STEP6 0x053e
|
||||
#define RT1015_BAT_RPO_STEP7 0x053f
|
||||
#define RT1015_BAT_RPO_STEP8 0x0540
|
||||
#define RT1015_BAT_RPO_STEP9 0x0541
|
||||
#define RT1015_BAT_RPO_STEP10 0x0542
|
||||
#define RT1015_BAT_RPO_STEP11 0x0543
|
||||
#define RT1015_BAT_RPO_STEP12 0x0544
|
||||
#define RT1015_SPREAD_SPEC1 0x0568
|
||||
#define RT1015_SPREAD_SPEC2 0x056a
|
||||
#define RT1015_PAD_STATUS 0x1000
|
||||
#define RT1015_PADS_PULLING_CTRL1 0x1002
|
||||
#define RT1015_PADS_DRIVING 0x1006
|
||||
#define RT1015_SYS_RST1 0x1007
|
||||
#define RT1015_SYS_RST2 0x1009
|
||||
#define RT1015_SYS_GATING1 0x100a
|
||||
#define RT1015_TEST_MODE1 0x100c
|
||||
#define RT1015_TEST_MODE2 0x100d
|
||||
#define RT1015_TIMING_CTRL1 0x100e
|
||||
#define RT1015_PLL_INT 0x1010
|
||||
#define RT1015_TEST_OUT1 0x1020
|
||||
#define RT1015_DC_CALIB_CLSD1 0x1200
|
||||
#define RT1015_DC_CALIB_CLSD2 0x1202
|
||||
#define RT1015_DC_CALIB_CLSD3 0x1204
|
||||
#define RT1015_DC_CALIB_CLSD4 0x1206
|
||||
#define RT1015_DC_CALIB_CLSD5 0x1208
|
||||
#define RT1015_DC_CALIB_CLSD6 0x120a
|
||||
#define RT1015_DC_CALIB_CLSD7 0x120c
|
||||
#define RT1015_DC_CALIB_CLSD8 0x120e
|
||||
#define RT1015_DC_CALIB_CLSD9 0x1210
|
||||
#define RT1015_DC_CALIB_CLSD10 0x1212
|
||||
#define RT1015_CLSD_INTERNAL1 0x1300
|
||||
#define RT1015_CLSD_INTERNAL2 0x1302
|
||||
#define RT1015_CLSD_INTERNAL3 0x1304
|
||||
#define RT1015_CLSD_INTERNAL4 0x1305
|
||||
#define RT1015_CLSD_INTERNAL5 0x1306
|
||||
#define RT1015_CLSD_INTERNAL6 0x1308
|
||||
#define RT1015_CLSD_INTERNAL7 0x130a
|
||||
#define RT1015_CLSD_INTERNAL8 0x130c
|
||||
#define RT1015_CLSD_INTERNAL9 0x130e
|
||||
#define RT1015_CLSD_OCP_CTRL 0x130f
|
||||
#define RT1015_VREF_LV 0x1310
|
||||
#define RT1015_MBIAS1 0x1312
|
||||
#define RT1015_MBIAS2 0x1314
|
||||
#define RT1015_MBIAS3 0x1316
|
||||
#define RT1015_MBIAS4 0x1318
|
||||
#define RT1015_VREF_LV1 0x131a
|
||||
#define RT1015_S_BST_TIMING_INTER1 0x1322
|
||||
#define RT1015_S_BST_TIMING_INTER2 0x1323
|
||||
#define RT1015_S_BST_TIMING_INTER3 0x1324
|
||||
#define RT1015_S_BST_TIMING_INTER4 0x1325
|
||||
#define RT1015_S_BST_TIMING_INTER5 0x1326
|
||||
#define RT1015_S_BST_TIMING_INTER6 0x1327
|
||||
#define RT1015_S_BST_TIMING_INTER7 0x1328
|
||||
#define RT1015_S_BST_TIMING_INTER8 0x1329
|
||||
#define RT1015_S_BST_TIMING_INTER9 0x132a
|
||||
#define RT1015_S_BST_TIMING_INTER10 0x132b
|
||||
#define RT1015_S_BST_TIMING_INTER11 0x1330
|
||||
#define RT1015_S_BST_TIMING_INTER12 0x1331
|
||||
#define RT1015_S_BST_TIMING_INTER13 0x1332
|
||||
#define RT1015_S_BST_TIMING_INTER14 0x1333
|
||||
#define RT1015_S_BST_TIMING_INTER15 0x1334
|
||||
#define RT1015_S_BST_TIMING_INTER16 0x1335
|
||||
#define RT1015_S_BST_TIMING_INTER17 0x1336
|
||||
#define RT1015_S_BST_TIMING_INTER18 0x1337
|
||||
#define RT1015_S_BST_TIMING_INTER19 0x1338
|
||||
#define RT1015_S_BST_TIMING_INTER20 0x1339
|
||||
#define RT1015_S_BST_TIMING_INTER21 0x133a
|
||||
#define RT1015_S_BST_TIMING_INTER22 0x133b
|
||||
#define RT1015_S_BST_TIMING_INTER23 0x133c
|
||||
#define RT1015_S_BST_TIMING_INTER24 0x133d
|
||||
#define RT1015_S_BST_TIMING_INTER25 0x133e
|
||||
#define RT1015_S_BST_TIMING_INTER26 0x133f
|
||||
#define RT1015_S_BST_TIMING_INTER27 0x1340
|
||||
#define RT1015_S_BST_TIMING_INTER28 0x1341
|
||||
#define RT1015_S_BST_TIMING_INTER29 0x1342
|
||||
#define RT1015_S_BST_TIMING_INTER30 0x1343
|
||||
#define RT1015_S_BST_TIMING_INTER31 0x1344
|
||||
#define RT1015_S_BST_TIMING_INTER32 0x1345
|
||||
#define RT1015_S_BST_TIMING_INTER33 0x1346
|
||||
#define RT1015_S_BST_TIMING_INTER34 0x1347
|
||||
#define RT1015_S_BST_TIMING_INTER35 0x1348
|
||||
#define RT1015_S_BST_TIMING_INTER36 0x1349
|
||||
|
||||
/* 0x0004 */
|
||||
#define RT1015_CLK_SYS_PRE_SEL_MASK (0x3 << 14)
|
||||
#define RT1015_CLK_SYS_PRE_SEL_SFT 14
|
||||
#define RT1015_CLK_SYS_PRE_SEL_MCLK (0x0 << 14)
|
||||
#define RT1015_CLK_SYS_PRE_SEL_PLL (0x2 << 14)
|
||||
#define RT1015_PLL_SEL_MASK (0x1 << 13)
|
||||
#define RT1015_PLL_SEL_SFT 13
|
||||
#define RT1015_PLL_SEL_PLL_SRC2 (0x0 << 13)
|
||||
#define RT1015_PLL_SEL_BCLK (0x1 << 13)
|
||||
#define RT1015_FS_PD_MASK (0x7 << 4)
|
||||
#define RT1015_FS_PD_SFT 4
|
||||
|
||||
/* 0x000a */
|
||||
#define RT1015_PLL_M_MAX 0xf
|
||||
#define RT1015_PLL_M_MASK (RT1015_PLL_M_MAX << 12)
|
||||
#define RT1015_PLL_M_SFT 12
|
||||
#define RT1015_PLL_M_BP (0x1 << 11)
|
||||
#define RT1015_PLL_M_BP_SFT 11
|
||||
#define RT1015_PLL_N_MAX 0x1ff
|
||||
#define RT1015_PLL_N_MASK (RT1015_PLL_N_MAX << 0)
|
||||
#define RT1015_PLL_N_SFT 0
|
||||
|
||||
/* 0x000c */
|
||||
#define RT1015_PLL_BPK_MASK (0x1 << 5)
|
||||
#define RT1015_PLL_BPK (0x0 << 5)
|
||||
#define RT1015_PLL_K_MAX 0x1f
|
||||
#define RT1015_PLL_K_MASK (RT1015_PLL_K_MAX)
|
||||
#define RT1015_PLL_K_SFT 0
|
||||
|
||||
/* 0x007a */
|
||||
#define RT1015_ID_MASK 0xff
|
||||
#define RT1015_ID_VERA 0x0
|
||||
#define RT1015_ID_VERB 0x1
|
||||
|
||||
/* 0x0102 */
|
||||
#define RT1015_DAC_VOL_MASK (0x7f << 9)
|
||||
#define RT1015_DAC_VOL_SFT 9
|
||||
|
||||
/* 0x0104 */
|
||||
#define RT1015_DAC_CLK (0x1 << 13)
|
||||
#define RT1015_DAC_CLK_BIT 13
|
||||
|
||||
/* 0x0106 */
|
||||
#define RT1015_DAC_MUTE_MASK (0x1 << 15)
|
||||
#define RT1015_DA_MUTE_SFT 15
|
||||
#define RT1015_DVOL_MUTE_FLAG_SFT 12
|
||||
|
||||
/* 0x0111 */
|
||||
#define RT1015_TCON_TDM_MS_MASK (0x1 << 14)
|
||||
#define RT1015_TCON_TDM_MS_SFT 14
|
||||
#define RT1015_TCON_TDM_MS_S (0x0 << 14)
|
||||
#define RT1015_TCON_TDM_MS_M (0x1 << 14)
|
||||
#define RT1015_I2S_DL_MASK (0x7 << 8)
|
||||
#define RT1015_I2S_DL_SFT 8
|
||||
#define RT1015_I2S_DL_16 (0x0 << 8)
|
||||
#define RT1015_I2S_DL_20 (0x1 << 8)
|
||||
#define RT1015_I2S_DL_24 (0x2 << 8)
|
||||
#define RT1015_I2S_DL_8 (0x3 << 8)
|
||||
#define RT1015_I2S_M_DF_MASK (0x7 << 0)
|
||||
#define RT1015_I2S_M_DF_SFT 0
|
||||
#define RT1015_I2S_M_DF_I2S (0x0)
|
||||
#define RT1015_I2S_M_DF_LEFT (0x1)
|
||||
#define RT1015_I2S_M_DF_PCM_A (0x2)
|
||||
#define RT1015_I2S_M_DF_PCM_B (0x3)
|
||||
#define RT1015_I2S_M_DF_PCM_A_N (0x6)
|
||||
#define RT1015_I2S_M_DF_PCM_B_N (0x7)
|
||||
|
||||
/* TDM_tcon Setting (0x0112) */
|
||||
#define RT1015_I2S_TCON_DF_MASK (0x7 << 13)
|
||||
#define RT1015_I2S_TCON_DF_SFT 13
|
||||
#define RT1015_I2S_TCON_DF_I2S (0x0 << 13)
|
||||
#define RT1015_I2S_TCON_DF_LEFT (0x1 << 13)
|
||||
#define RT1015_I2S_TCON_DF_PCM_A (0x2 << 13)
|
||||
#define RT1015_I2S_TCON_DF_PCM_B (0x3 << 13)
|
||||
#define RT1015_I2S_TCON_DF_PCM_A_N (0x6 << 13)
|
||||
#define RT1015_I2S_TCON_DF_PCM_B_N (0x7 << 13)
|
||||
#define RT1015_TCON_BCLK_SEL_MASK (0x3 << 10)
|
||||
#define RT1015_TCON_BCLK_SEL_SFT 10
|
||||
#define RT1015_TCON_BCLK_SEL_32FS (0x0 << 10)
|
||||
#define RT1015_TCON_BCLK_SEL_64FS (0x1 << 10)
|
||||
#define RT1015_TCON_BCLK_SEL_128FS (0x2 << 10)
|
||||
#define RT1015_TCON_BCLK_SEL_256FS (0x3 << 10)
|
||||
#define RT1015_TCON_CH_LEN_MASK (0x3 << 5)
|
||||
#define RT1015_TCON_CH_LEN_SFT 5
|
||||
#define RT1015_TCON_CH_LEN_16B (0x0 << 5)
|
||||
#define RT1015_TCON_CH_LEN_20B (0x1 << 5)
|
||||
#define RT1015_TCON_CH_LEN_24B (0x2 << 5)
|
||||
#define RT1015_TCON_CH_LEN_32B (0x3 << 5)
|
||||
#define RT1015_TCON_BCLK_MST_MASK (0x1 << 4)
|
||||
#define RT1015_TCON_BCLK_MST_SFT 4
|
||||
#define RT1015_TCON_BCLK_MST_INV (0x1 << 4)
|
||||
|
||||
/* TDM1 Setting-1 (0x0114) */
|
||||
#define RT1015_TDM_INV_BCLK_MASK (0x1 << 15)
|
||||
#define RT1015_TDM_INV_BCLK_SFT 15
|
||||
#define RT1015_TDM_INV_BCLK (0x1 << 15)
|
||||
|
||||
/* 0x0330 */
|
||||
#define RT1015_ABST_AUTO_EN_MASK (0x1 << 13)
|
||||
#define RT1015_ABST_AUTO_MODE (0x1 << 13)
|
||||
#define RT1015_ABST_REG_MODE (0x0 << 13)
|
||||
#define RT1015_ABST_FIX_TGT_MASK (0x1 << 12)
|
||||
#define RT1015_ABST_FIX_TGT_EN (0x1 << 12)
|
||||
#define RT1015_ABST_FIX_TGT_DIS (0x0 << 12)
|
||||
#define RT1015_BYPASS_SWR_REG_MASK (0x1 << 7)
|
||||
#define RT1015_BYPASS_SWRREG_BYPASS (0x1 << 7)
|
||||
#define RT1015_BYPASS_SWRREG_PASS (0x0 << 7)
|
||||
|
||||
/* 0x0322 */
|
||||
#define RT1015_PWR_LDO2 (0x1 << 15)
|
||||
#define RT1015_PWR_LDO2_BIT 15
|
||||
#define RT1015_PWR_DAC (0x1 << 14)
|
||||
#define RT1015_PWR_DAC_BIT 14
|
||||
#define RT1015_PWR_INTCLK (0x1 << 13)
|
||||
#define RT1015_PWR_INTCLK_BIT 13
|
||||
#define RT1015_PWR_ISENSE (0x1 << 12)
|
||||
#define RT1015_PWR_ISENSE_BIT 12
|
||||
#define RT1015_PWR_VSENSE (0x1 << 10)
|
||||
#define RT1015_PWR_VSENSE_BIT 10
|
||||
#define RT1015_PWR_PLL (0x1 << 9)
|
||||
#define RT1015_PWR_PLL_BIT 9
|
||||
#define RT1015_PWR_BG_1_2 (0x1 << 8)
|
||||
#define RT1015_PWR_BG_1_2_BIT 8
|
||||
#define RT1015_PWR_MBIAS_BG (0x1 << 7)
|
||||
#define RT1015_PWR_MBIAS_BG_BIT 7
|
||||
#define RT1015_PWR_VBAT (0x1 << 6)
|
||||
#define RT1015_PWR_VBAT_BIT 6
|
||||
#define RT1015_PWR_MBIAS (0x1 << 4)
|
||||
#define RT1015_PWR_MBIAS_BIT 4
|
||||
#define RT1015_PWR_ADCV (0x1 << 3)
|
||||
#define RT1015_PWR_ADCV_BIT 3
|
||||
#define RT1015_PWR_MIXERV (0x1 << 2)
|
||||
#define RT1015_PWR_MIXERV_BIT 2
|
||||
#define RT1015_PWR_SUMV (0x1 << 1)
|
||||
#define RT1015_PWR_SUMV_BIT 1
|
||||
#define RT1015_PWR_VREFLV (0x1 << 0)
|
||||
#define RT1015_PWR_VREFLV_BIT 0
|
||||
|
||||
/* 0x0324 */
|
||||
#define RT1015_PWR_BASIC (0x1 << 15)
|
||||
#define RT1015_PWR_BASIC_BIT 15
|
||||
#define RT1015_PWR_SD (0x1 << 14)
|
||||
#define RT1015_PWR_SD_BIT 14
|
||||
#define RT1015_PWR_IBIAS (0x1 << 13)
|
||||
#define RT1015_PWR_IBIAS_BIT 13
|
||||
#define RT1015_PWR_VCM (0x1 << 11)
|
||||
#define RT1015_PWR_VCM_BIT 11
|
||||
|
||||
/* 0x0328 */
|
||||
#define RT1015_PWR_SWR (0x1 << 12)
|
||||
#define RT1015_PWR_SWR_BIT 12
|
||||
|
||||
/* 0x1300 */
|
||||
#define RT1015_PWR_CLSD (0x1 << 12)
|
||||
#define RT1015_PWR_CLSD_BIT 12
|
||||
|
||||
/* 0x007a */
|
||||
#define RT1015_ID_MASK 0xff
|
||||
#define RT1015_ID_VERA 0x0
|
||||
#define RT1015_ID_VERB 0x1
|
||||
|
||||
/* System Clock Source */
|
||||
enum {
|
||||
RT1015_SCLK_S_MCLK,
|
||||
RT1015_SCLK_S_PLL,
|
||||
};
|
||||
|
||||
/* PLL1 Source */
|
||||
enum {
|
||||
RT1015_PLL_S_MCLK,
|
||||
RT1015_PLL_S_BCLK,
|
||||
};
|
||||
|
||||
enum {
|
||||
RT1015_AIF1,
|
||||
RT1015_AIFS,
|
||||
};
|
||||
|
||||
enum {
|
||||
RT1015_VERA,
|
||||
RT1015_VERB,
|
||||
};
|
||||
|
||||
enum {
|
||||
BYPASS,
|
||||
ADAPTIVE,
|
||||
FIXED_ADAPTIVE,
|
||||
};
|
||||
|
||||
struct rt1015_priv {
|
||||
struct snd_soc_component *component;
|
||||
struct regmap *regmap;
|
||||
int sysclk;
|
||||
int sysclk_src;
|
||||
int lrck;
|
||||
int bclk;
|
||||
int id;
|
||||
int pll_src;
|
||||
int pll_in;
|
||||
int pll_out;
|
||||
int boost_mode;
|
||||
int bypass_boost;
|
||||
int amp_ver;
|
||||
int dac_is_used;
|
||||
};
|
||||
|
||||
#endif /* __RT1015_H__ */
|
736
sound/soc/codecs/rt1308-sdw.c
Normal file
736
sound/soc/codecs/rt1308-sdw.c
Normal file
@ -0,0 +1,736 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// rt1308-sdw.c -- rt1308 ALSA SoC audio driver
|
||||
//
|
||||
// Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
//
|
||||
//
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "rt1308.h"
|
||||
#include "rt1308-sdw.h"
|
||||
|
||||
static bool rt1308_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00e0:
|
||||
case 0x00f0:
|
||||
case 0x2f01 ... 0x2f07:
|
||||
case 0x3000 ... 0x3001:
|
||||
case 0x3004 ... 0x3005:
|
||||
case 0x3008:
|
||||
case 0x300a:
|
||||
case 0xc000 ... 0xcff3:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x2f01 ... 0x2f07:
|
||||
case 0x3000 ... 0x3001:
|
||||
case 0x3004 ... 0x3005:
|
||||
case 0x3008:
|
||||
case 0x300a:
|
||||
case 0xc000:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config rt1308_sdw_regmap = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 8,
|
||||
.readable_reg = rt1308_readable_register,
|
||||
.volatile_reg = rt1308_volatile_register,
|
||||
.max_register = 0xcfff,
|
||||
.reg_defaults = rt1308_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
/* Bus clock frequency */
|
||||
#define RT1308_CLK_FREQ_9600000HZ 9600000
|
||||
#define RT1308_CLK_FREQ_12000000HZ 12000000
|
||||
#define RT1308_CLK_FREQ_6000000HZ 6000000
|
||||
#define RT1308_CLK_FREQ_4800000HZ 4800000
|
||||
#define RT1308_CLK_FREQ_2400000HZ 2400000
|
||||
#define RT1308_CLK_FREQ_12288000HZ 12288000
|
||||
|
||||
static int rt1308_clock_config(struct device *dev)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
|
||||
unsigned int clk_freq, value;
|
||||
|
||||
clk_freq = (rt1308->params.curr_dr_freq >> 1);
|
||||
|
||||
switch (clk_freq) {
|
||||
case RT1308_CLK_FREQ_12000000HZ:
|
||||
value = 0x0;
|
||||
break;
|
||||
case RT1308_CLK_FREQ_6000000HZ:
|
||||
value = 0x1;
|
||||
break;
|
||||
case RT1308_CLK_FREQ_9600000HZ:
|
||||
value = 0x2;
|
||||
break;
|
||||
case RT1308_CLK_FREQ_4800000HZ:
|
||||
value = 0x3;
|
||||
break;
|
||||
case RT1308_CLK_FREQ_2400000HZ:
|
||||
value = 0x4;
|
||||
break;
|
||||
case RT1308_CLK_FREQ_12288000HZ:
|
||||
value = 0x5;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(rt1308->regmap, 0xe0, value);
|
||||
regmap_write(rt1308->regmap, 0xf0, value);
|
||||
|
||||
dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1308_read_prop(struct sdw_slave *slave)
|
||||
{
|
||||
struct sdw_slave_prop *prop = &slave->prop;
|
||||
int nval, i, num_of_ports = 1;
|
||||
u32 bit;
|
||||
unsigned long addr;
|
||||
struct sdw_dpn_prop *dpn;
|
||||
|
||||
prop->paging_support = true;
|
||||
|
||||
/* first we need to allocate memory for set bits in port lists */
|
||||
prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */
|
||||
prop->sink_ports = 0x2; /* BITMAP: 00000010 */
|
||||
|
||||
/* for sink */
|
||||
nval = hweight32(prop->sink_ports);
|
||||
num_of_ports += nval;
|
||||
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->sink_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->sink_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
dpn = prop->sink_dpn_prop;
|
||||
addr = prop->sink_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].type = SDW_DPN_FULL;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Allocate port_ready based on num_of_ports */
|
||||
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
|
||||
sizeof(*slave->port_ready),
|
||||
GFP_KERNEL);
|
||||
if (!slave->port_ready)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize completion */
|
||||
for (i = 0; i < num_of_ports; i++)
|
||||
init_completion(&slave->port_ready[i]);
|
||||
|
||||
/* set the timeout values */
|
||||
prop->clk_stop_timeout = 20;
|
||||
|
||||
dev_dbg(&slave->dev, "%s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1308_io_init(struct device *dev, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp;
|
||||
unsigned int efuse_c_btl_l, efuse_c_btl_r;
|
||||
|
||||
if (rt1308->hw_init)
|
||||
return 0;
|
||||
|
||||
ret = rt1308_read_prop(slave);
|
||||
if (ret < 0)
|
||||
goto _io_init_err_;
|
||||
|
||||
if (rt1308->first_hw_init) {
|
||||
regcache_cache_only(rt1308->regmap, false);
|
||||
regcache_cache_bypass(rt1308->regmap, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* PM runtime is only enabled when a Slave reports as Attached
|
||||
*/
|
||||
if (!rt1308->first_hw_init) {
|
||||
/* set autosuspend parameters */
|
||||
pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
|
||||
pm_runtime_use_autosuspend(&slave->dev);
|
||||
|
||||
/* update count of parent 'active' children */
|
||||
pm_runtime_set_active(&slave->dev);
|
||||
|
||||
/* make sure the device does not suspend immediately */
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
|
||||
pm_runtime_enable(&slave->dev);
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&slave->dev);
|
||||
|
||||
/* sw reset */
|
||||
regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0);
|
||||
|
||||
/* read efuse */
|
||||
regmap_write(rt1308->regmap, 0xc360, 0x01);
|
||||
regmap_write(rt1308->regmap, 0xc361, 0x80);
|
||||
regmap_write(rt1308->regmap, 0xc7f0, 0x04);
|
||||
regmap_write(rt1308->regmap, 0xc7f1, 0xfe);
|
||||
msleep(100);
|
||||
regmap_write(rt1308->regmap, 0xc7f0, 0x44);
|
||||
msleep(20);
|
||||
regmap_write(rt1308->regmap, 0xc240, 0x10);
|
||||
|
||||
regmap_read(rt1308->regmap, 0xc861, &tmp);
|
||||
efuse_m_btl_l = tmp;
|
||||
regmap_read(rt1308->regmap, 0xc860, &tmp);
|
||||
efuse_m_btl_l = efuse_m_btl_l | (tmp << 8);
|
||||
regmap_read(rt1308->regmap, 0xc863, &tmp);
|
||||
efuse_c_btl_l = tmp;
|
||||
regmap_read(rt1308->regmap, 0xc862, &tmp);
|
||||
efuse_c_btl_l = efuse_c_btl_l | (tmp << 8);
|
||||
regmap_read(rt1308->regmap, 0xc871, &tmp);
|
||||
efuse_m_btl_r = tmp;
|
||||
regmap_read(rt1308->regmap, 0xc870, &tmp);
|
||||
efuse_m_btl_r = efuse_m_btl_r | (tmp << 8);
|
||||
regmap_read(rt1308->regmap, 0xc873, &tmp);
|
||||
efuse_c_btl_r = tmp;
|
||||
regmap_read(rt1308->regmap, 0xc872, &tmp);
|
||||
efuse_c_btl_r = efuse_c_btl_r | (tmp << 8);
|
||||
dev_info(&slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__,
|
||||
efuse_m_btl_l, efuse_m_btl_r);
|
||||
dev_info(&slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__,
|
||||
efuse_c_btl_l, efuse_c_btl_r);
|
||||
|
||||
/* initial settings */
|
||||
regmap_write(rt1308->regmap, 0xc103, 0xc0);
|
||||
regmap_write(rt1308->regmap, 0xc030, 0x17);
|
||||
regmap_write(rt1308->regmap, 0xc031, 0x81);
|
||||
regmap_write(rt1308->regmap, 0xc032, 0x26);
|
||||
regmap_write(rt1308->regmap, 0xc040, 0x80);
|
||||
regmap_write(rt1308->regmap, 0xc041, 0x80);
|
||||
regmap_write(rt1308->regmap, 0xc042, 0x06);
|
||||
regmap_write(rt1308->regmap, 0xc052, 0x0a);
|
||||
regmap_write(rt1308->regmap, 0xc080, 0x0a);
|
||||
regmap_write(rt1308->regmap, 0xc060, 0x02);
|
||||
regmap_write(rt1308->regmap, 0xc061, 0x75);
|
||||
regmap_write(rt1308->regmap, 0xc062, 0x05);
|
||||
regmap_write(rt1308->regmap, 0xc171, 0x07);
|
||||
regmap_write(rt1308->regmap, 0xc173, 0x0d);
|
||||
regmap_write(rt1308->regmap, 0xc311, 0x7f);
|
||||
regmap_write(rt1308->regmap, 0xc900, 0x90);
|
||||
regmap_write(rt1308->regmap, 0xc1a0, 0x84);
|
||||
regmap_write(rt1308->regmap, 0xc1a1, 0x01);
|
||||
regmap_write(rt1308->regmap, 0xc360, 0x78);
|
||||
regmap_write(rt1308->regmap, 0xc361, 0x87);
|
||||
regmap_write(rt1308->regmap, 0xc0a1, 0x71);
|
||||
regmap_write(rt1308->regmap, 0xc210, 0x00);
|
||||
regmap_write(rt1308->regmap, 0xc070, 0x00);
|
||||
regmap_write(rt1308->regmap, 0xc100, 0xd7);
|
||||
regmap_write(rt1308->regmap, 0xc101, 0xd7);
|
||||
regmap_write(rt1308->regmap, 0xc300, 0x09);
|
||||
|
||||
if (rt1308->first_hw_init) {
|
||||
regcache_cache_bypass(rt1308->regmap, false);
|
||||
regcache_mark_dirty(rt1308->regmap);
|
||||
} else
|
||||
rt1308->first_hw_init = true;
|
||||
|
||||
/* Mark Slave initialization complete */
|
||||
rt1308->hw_init = true;
|
||||
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
pm_runtime_put_autosuspend(&slave->dev);
|
||||
|
||||
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
|
||||
|
||||
_io_init_err_:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt1308_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
/* Update the status */
|
||||
rt1308->status = status;
|
||||
|
||||
if (status == SDW_SLAVE_UNATTACHED)
|
||||
rt1308->hw_init = false;
|
||||
|
||||
/*
|
||||
* Perform initialization only if slave status is present and
|
||||
* hw_init flag is false
|
||||
*/
|
||||
if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED)
|
||||
return 0;
|
||||
|
||||
/* perform I/O transfers required for Slave initialization */
|
||||
return rt1308_io_init(&slave->dev, slave);
|
||||
}
|
||||
|
||||
static int rt1308_bus_config(struct sdw_slave *slave,
|
||||
struct sdw_bus_params *params)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
|
||||
int ret;
|
||||
|
||||
memcpy(&rt1308->params, params, sizeof(*params));
|
||||
|
||||
ret = rt1308_clock_config(&slave->dev);
|
||||
if (ret < 0)
|
||||
dev_err(&slave->dev, "Invalid clk config");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt1308_interrupt_callback(struct sdw_slave *slave,
|
||||
struct sdw_slave_intr_status *status)
|
||||
{
|
||||
dev_dbg(&slave->dev,
|
||||
"%s control_port_stat=%x", __func__, status->control_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
msleep(30);
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
|
||||
0x3, 0x3);
|
||||
msleep(40);
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
|
||||
0x3, 0);
|
||||
usleep_range(150000, 200000);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const rt1308_rx_data_ch_select[] = {
|
||||
"LR",
|
||||
"LL",
|
||||
"RL",
|
||||
"RR",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum,
|
||||
RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0,
|
||||
rt1308_rx_data_ch_select);
|
||||
|
||||
static const struct snd_kcontrol_new rt1308_snd_controls[] = {
|
||||
|
||||
/* I2S Data Channel Selection */
|
||||
SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rt1308_sto_dac_l =
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
|
||||
RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
|
||||
RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new rt1308_sto_dac_r =
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
|
||||
RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
|
||||
RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
|
||||
|
||||
static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
|
||||
/* Audio Interface */
|
||||
SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
/* Supply Widgets */
|
||||
SND_SOC_DAPM_SUPPLY("MBIAS20U",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ALDO",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DBG",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DACL",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("CLK25M",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC_R",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC_L",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DAC Power",
|
||||
RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DLDO",
|
||||
RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VREF",
|
||||
RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MIXER_R",
|
||||
RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MIXER_L",
|
||||
RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MBIAS4U",
|
||||
RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PLL2_LDO",
|
||||
RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL2B",
|
||||
RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL2F",
|
||||
RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL2F2",
|
||||
RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL2B2",
|
||||
RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0),
|
||||
|
||||
/* Digital Interface */
|
||||
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
|
||||
SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
|
||||
|
||||
/* Output Lines */
|
||||
SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
|
||||
rt1308_classd_event,
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_OUTPUT("SPOL"),
|
||||
SND_SOC_DAPM_OUTPUT("SPOR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
|
||||
|
||||
{ "DAC", NULL, "AIF1RX" },
|
||||
|
||||
{ "DAC", NULL, "MBIAS20U" },
|
||||
{ "DAC", NULL, "ALDO" },
|
||||
{ "DAC", NULL, "DBG" },
|
||||
{ "DAC", NULL, "DACL" },
|
||||
{ "DAC", NULL, "CLK25M" },
|
||||
{ "DAC", NULL, "ADC_R" },
|
||||
{ "DAC", NULL, "ADC_L" },
|
||||
{ "DAC", NULL, "DLDO" },
|
||||
{ "DAC", NULL, "VREF" },
|
||||
{ "DAC", NULL, "MIXER_R" },
|
||||
{ "DAC", NULL, "MIXER_L" },
|
||||
{ "DAC", NULL, "MBIAS4U" },
|
||||
{ "DAC", NULL, "PLL2_LDO" },
|
||||
{ "DAC", NULL, "PLL2B" },
|
||||
{ "DAC", NULL, "PLL2F" },
|
||||
{ "DAC", NULL, "PLL2F2" },
|
||||
{ "DAC", NULL, "PLL2B2" },
|
||||
|
||||
{ "DAC L", "Switch", "DAC" },
|
||||
{ "DAC R", "Switch", "DAC" },
|
||||
{ "DAC L", NULL, "DAC Power" },
|
||||
{ "DAC R", NULL, "DAC Power" },
|
||||
|
||||
{ "CLASS D", NULL, "DAC L" },
|
||||
{ "CLASS D", NULL, "DAC R" },
|
||||
{ "SPOL", NULL, "CLASS D" },
|
||||
{ "SPOR", NULL, "CLASS D" },
|
||||
};
|
||||
|
||||
static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
|
||||
int direction)
|
||||
{
|
||||
struct sdw_stream_data *stream;
|
||||
|
||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (!stream)
|
||||
return -ENOMEM;
|
||||
|
||||
stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream;
|
||||
|
||||
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dai->playback_dma_data = stream;
|
||||
else
|
||||
dai->capture_dma_data = stream;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sdw_stream_data *stream;
|
||||
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
kfree(stream);
|
||||
}
|
||||
|
||||
static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1308_sdw_priv *rt1308 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_data *stream;
|
||||
int retval, port, num_channels;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rt1308->sdw_slave)
|
||||
return -EINVAL;
|
||||
|
||||
/* SoundWire specific configuration */
|
||||
/* port 1 for playback */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
port = 1;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = (1 << (num_channels)) - 1;
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config,
|
||||
&port_config, 1, stream->sdw_stream);
|
||||
if (retval) {
|
||||
dev_err(dai->dev, "Unable to configure port\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1308_sdw_priv *rt1308 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_data *stream =
|
||||
snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!rt1308->sdw_slave)
|
||||
return -EINVAL;
|
||||
|
||||
sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* slave_ops: callbacks for get_clock_stop_mode, clock_stop and
|
||||
* port_prep are not defined for now
|
||||
*/
|
||||
static struct sdw_slave_ops rt1308_slave_ops = {
|
||||
.read_prop = rt1308_read_prop,
|
||||
.interrupt_callback = rt1308_interrupt_callback,
|
||||
.update_status = rt1308_update_status,
|
||||
.bus_config = rt1308_bus_config,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
|
||||
.controls = rt1308_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(rt1308_snd_controls),
|
||||
.dapm_widgets = rt1308_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
|
||||
.dapm_routes = rt1308_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
|
||||
.hw_params = rt1308_sdw_hw_params,
|
||||
.hw_free = rt1308_sdw_pcm_hw_free,
|
||||
.set_sdw_stream = rt1308_set_sdw_stream,
|
||||
.shutdown = rt1308_sdw_shutdown,
|
||||
};
|
||||
|
||||
#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
|
||||
#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
static struct snd_soc_dai_driver rt1308_sdw_dai[] = {
|
||||
{
|
||||
.name = "rt1308-aif",
|
||||
.playback = {
|
||||
.stream_name = "DP1 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RT1308_STEREO_RATES,
|
||||
.formats = RT1308_FORMATS,
|
||||
},
|
||||
.ops = &rt1308_aif_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int rt1308_sdw_init(struct device *dev, struct regmap *regmap,
|
||||
struct sdw_slave *slave)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308;
|
||||
int ret;
|
||||
|
||||
rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL);
|
||||
if (!rt1308)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, rt1308);
|
||||
rt1308->sdw_slave = slave;
|
||||
rt1308->regmap = regmap;
|
||||
|
||||
/*
|
||||
* Mark hw_init to false
|
||||
* HW init will be performed when device reports present
|
||||
*/
|
||||
rt1308->hw_init = false;
|
||||
rt1308->first_hw_init = false;
|
||||
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&soc_component_sdw_rt1308,
|
||||
rt1308_sdw_dai,
|
||||
ARRAY_SIZE(rt1308_sdw_dai));
|
||||
|
||||
dev_dbg(&slave->dev, "%s\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt1308_sdw_probe(struct sdw_slave *slave,
|
||||
const struct sdw_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
/* Assign ops */
|
||||
slave->ops = &rt1308_slave_ops;
|
||||
|
||||
/* Regmap Initialization */
|
||||
regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap);
|
||||
if (!regmap)
|
||||
return -EINVAL;
|
||||
|
||||
rt1308_sdw_init(&slave->dev, regmap, slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdw_device_id rt1308_id[] = {
|
||||
SDW_SLAVE_ENTRY(0x025d, 0x1308, 0),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdw, rt1308_id);
|
||||
|
||||
static int rt1308_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
|
||||
|
||||
if (!rt1308->hw_init)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(rt1308->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT1308_PROBE_TIMEOUT 2000
|
||||
|
||||
static int rt1308_dev_resume(struct device *dev)
|
||||
{
|
||||
struct sdw_slave *slave = dev_to_sdw_dev(dev);
|
||||
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
if (!rt1308->hw_init)
|
||||
return 0;
|
||||
|
||||
if (!slave->unattach_request)
|
||||
goto regmap_sync;
|
||||
|
||||
time = wait_for_completion_timeout(&slave->initialization_complete,
|
||||
msecs_to_jiffies(RT1308_PROBE_TIMEOUT));
|
||||
if (!time) {
|
||||
dev_err(&slave->dev, "Initialization not complete, timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_sync:
|
||||
slave->unattach_request = 0;
|
||||
regcache_cache_only(rt1308->regmap, false);
|
||||
regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rt1308_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume)
|
||||
SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL)
|
||||
};
|
||||
|
||||
static struct sdw_driver rt1308_sdw_driver = {
|
||||
.driver = {
|
||||
.name = "rt1308",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rt1308_pm,
|
||||
},
|
||||
.probe = rt1308_sdw_probe,
|
||||
.ops = &rt1308_slave_ops,
|
||||
.id_table = rt1308_id,
|
||||
};
|
||||
module_sdw_driver(rt1308_sdw_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT1308 driver SDW");
|
||||
MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
169
sound/soc/codecs/rt1308-sdw.h
Normal file
169
sound/soc/codecs/rt1308-sdw.h
Normal file
@ -0,0 +1,169 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt1308-sdw.h -- RT1308 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT1308_SDW_H__
|
||||
#define __RT1308_SDW_H__
|
||||
|
||||
static const struct reg_default rt1308_reg_defaults[] = {
|
||||
{ 0x0000, 0x00 },
|
||||
{ 0x0001, 0x00 },
|
||||
{ 0x0002, 0x00 },
|
||||
{ 0x0003, 0x00 },
|
||||
{ 0x0004, 0x00 },
|
||||
{ 0x0005, 0x01 },
|
||||
{ 0x0020, 0x00 },
|
||||
{ 0x0022, 0x00 },
|
||||
{ 0x0023, 0x00 },
|
||||
{ 0x0024, 0x00 },
|
||||
{ 0x0025, 0x00 },
|
||||
{ 0x0026, 0x00 },
|
||||
{ 0x0030, 0x00 },
|
||||
{ 0x0032, 0x00 },
|
||||
{ 0x0033, 0x00 },
|
||||
{ 0x0034, 0x00 },
|
||||
{ 0x0035, 0x00 },
|
||||
{ 0x0036, 0x00 },
|
||||
{ 0x0040, 0x00 },
|
||||
{ 0x0041, 0x00 },
|
||||
{ 0x0042, 0x00 },
|
||||
{ 0x0043, 0x00 },
|
||||
{ 0x0044, 0x20 },
|
||||
{ 0x0045, 0x01 },
|
||||
{ 0x0046, 0x01 },
|
||||
{ 0x0048, 0x00 },
|
||||
{ 0x0049, 0x00 },
|
||||
{ 0x0050, 0x20 },
|
||||
{ 0x0051, 0x02 },
|
||||
{ 0x0052, 0x5D },
|
||||
{ 0x0053, 0x13 },
|
||||
{ 0x0054, 0x08 },
|
||||
{ 0x0055, 0x00 },
|
||||
{ 0x0060, 0x00 },
|
||||
{ 0x0070, 0x00 },
|
||||
{ 0x00E0, 0x00 },
|
||||
{ 0x00F0, 0x00 },
|
||||
{ 0x0100, 0x00 },
|
||||
{ 0x0101, 0x00 },
|
||||
{ 0x0102, 0x20 },
|
||||
{ 0x0103, 0x00 },
|
||||
{ 0x0104, 0x00 },
|
||||
{ 0x0105, 0x03 },
|
||||
{ 0x0120, 0x00 },
|
||||
{ 0x0122, 0x00 },
|
||||
{ 0x0123, 0x00 },
|
||||
{ 0x0124, 0x00 },
|
||||
{ 0x0125, 0x00 },
|
||||
{ 0x0126, 0x00 },
|
||||
{ 0x0127, 0x00 },
|
||||
{ 0x0130, 0x00 },
|
||||
{ 0x0132, 0x00 },
|
||||
{ 0x0133, 0x00 },
|
||||
{ 0x0134, 0x00 },
|
||||
{ 0x0135, 0x00 },
|
||||
{ 0x0136, 0x00 },
|
||||
{ 0x0137, 0x00 },
|
||||
{ 0x0200, 0x00 },
|
||||
{ 0x0201, 0x00 },
|
||||
{ 0x0202, 0x00 },
|
||||
{ 0x0203, 0x00 },
|
||||
{ 0x0204, 0x00 },
|
||||
{ 0x0205, 0x03 },
|
||||
{ 0x0220, 0x00 },
|
||||
{ 0x0222, 0x00 },
|
||||
{ 0x0223, 0x00 },
|
||||
{ 0x0224, 0x00 },
|
||||
{ 0x0225, 0x00 },
|
||||
{ 0x0226, 0x00 },
|
||||
{ 0x0227, 0x00 },
|
||||
{ 0x0230, 0x00 },
|
||||
{ 0x0232, 0x00 },
|
||||
{ 0x0233, 0x00 },
|
||||
{ 0x0234, 0x00 },
|
||||
{ 0x0235, 0x00 },
|
||||
{ 0x0236, 0x00 },
|
||||
{ 0x0237, 0x00 },
|
||||
{ 0x0400, 0x00 },
|
||||
{ 0x0401, 0x00 },
|
||||
{ 0x0402, 0x00 },
|
||||
{ 0x0403, 0x00 },
|
||||
{ 0x0404, 0x00 },
|
||||
{ 0x0405, 0x03 },
|
||||
{ 0x0420, 0x00 },
|
||||
{ 0x0422, 0x00 },
|
||||
{ 0x0423, 0x00 },
|
||||
{ 0x0424, 0x00 },
|
||||
{ 0x0425, 0x00 },
|
||||
{ 0x0426, 0x00 },
|
||||
{ 0x0427, 0x00 },
|
||||
{ 0x0430, 0x00 },
|
||||
{ 0x0432, 0x00 },
|
||||
{ 0x0433, 0x00 },
|
||||
{ 0x0434, 0x00 },
|
||||
{ 0x0435, 0x00 },
|
||||
{ 0x0436, 0x00 },
|
||||
{ 0x0437, 0x00 },
|
||||
{ 0x0f00, 0x00 },
|
||||
{ 0x0f01, 0x00 },
|
||||
{ 0x0f02, 0x00 },
|
||||
{ 0x0f03, 0x00 },
|
||||
{ 0x0f04, 0x00 },
|
||||
{ 0x0f05, 0x00 },
|
||||
{ 0x0f20, 0x00 },
|
||||
{ 0x0f22, 0x00 },
|
||||
{ 0x0f23, 0x00 },
|
||||
{ 0x0f24, 0x00 },
|
||||
{ 0x0f25, 0x00 },
|
||||
{ 0x0f26, 0x00 },
|
||||
{ 0x0f27, 0x00 },
|
||||
{ 0x0f30, 0x00 },
|
||||
{ 0x0f32, 0x00 },
|
||||
{ 0x0f33, 0x00 },
|
||||
{ 0x0f34, 0x00 },
|
||||
{ 0x0f35, 0x00 },
|
||||
{ 0x0f36, 0x00 },
|
||||
{ 0x0f37, 0x00 },
|
||||
{ 0x2f01, 0x01 },
|
||||
{ 0x2f02, 0x09 },
|
||||
{ 0x2f03, 0x00 },
|
||||
{ 0x2f04, 0x0f },
|
||||
{ 0x2f05, 0x0b },
|
||||
{ 0x2f06, 0x01 },
|
||||
{ 0x2f07, 0x8e },
|
||||
{ 0x3000, 0x00 },
|
||||
{ 0x3001, 0x00 },
|
||||
{ 0x3004, 0x01 },
|
||||
{ 0x3005, 0x23 },
|
||||
{ 0x3008, 0x02 },
|
||||
{ 0x300a, 0x00 },
|
||||
{ 0xc003 | (RT1308_DAC_SET << 4), 0x00 },
|
||||
{ 0xc001 | (RT1308_POWER << 4), 0x00 },
|
||||
{ 0xc002 | (RT1308_POWER << 4), 0x00 },
|
||||
};
|
||||
|
||||
#define RT1308_SDW_OFFSET 0xc000
|
||||
#define RT1308_SDW_OFFSET_BYTE0 0xc000
|
||||
#define RT1308_SDW_OFFSET_BYTE1 0xc001
|
||||
#define RT1308_SDW_OFFSET_BYTE2 0xc002
|
||||
#define RT1308_SDW_OFFSET_BYTE3 0xc003
|
||||
|
||||
#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4))
|
||||
|
||||
struct rt1308_sdw_priv {
|
||||
struct snd_soc_component *component;
|
||||
struct regmap *regmap;
|
||||
struct sdw_slave *sdw_slave;
|
||||
enum sdw_slave_status status;
|
||||
struct sdw_bus_params params;
|
||||
bool hw_init;
|
||||
bool first_hw_init;
|
||||
};
|
||||
|
||||
struct sdw_stream_data {
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
};
|
||||
|
||||
#endif /* __RT1308_SDW_H__ */
|
@ -215,11 +215,9 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component,
|
||||
{
|
||||
struct rt5514_dsp *rt5514_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
u8 buf[8];
|
||||
|
||||
mutex_lock(&rt5514_dsp->dma_lock);
|
||||
ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
rt5514_dsp->substream = substream;
|
||||
rt5514_dsp->dma_offset = 0;
|
||||
|
||||
@ -230,7 +228,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component,
|
||||
|
||||
mutex_unlock(&rt5514_dsp->dma_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5514_spi_hw_free(struct snd_soc_component *component,
|
||||
@ -245,7 +243,7 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component,
|
||||
|
||||
cancel_delayed_work_sync(&rt5514_dsp->copy_work);
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
|
||||
@ -294,8 +292,8 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
|
||||
static int rt5514_spi_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -132,14 +132,12 @@ static int rt5677_spi_hw_params(
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&rt5677_dsp->dma_lock);
|
||||
ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
rt5677_dsp->substream = substream;
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_spi_hw_free(
|
||||
@ -153,7 +151,7 @@ static int rt5677_spi_hw_free(
|
||||
rt5677_dsp->substream = NULL;
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_spi_prepare(
|
||||
@ -376,8 +374,8 @@ done:
|
||||
static int rt5677_spi_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
|
||||
NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
551
sound/soc/codecs/rt700-sdw.c
Normal file
551
sound/soc/codecs/rt700-sdw.c
Normal file
@ -0,0 +1,551 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// rt700-sdw.c -- rt700 ALSA SoC audio driver
|
||||
//
|
||||
// Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
//
|
||||
//
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include "rt700.h"
|
||||
#include "rt700-sdw.h"
|
||||
|
||||
static bool rt700_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00e0:
|
||||
case 0x00f0:
|
||||
case 0x2000 ... 0x200e:
|
||||
case 0x2012 ... 0x2016:
|
||||
case 0x201a ... 0x2027:
|
||||
case 0x2029 ... 0x202a:
|
||||
case 0x202d ... 0x2034:
|
||||
case 0x2200 ... 0x2204:
|
||||
case 0x2206 ... 0x2212:
|
||||
case 0x2220 ... 0x2223:
|
||||
case 0x2230 ... 0x2231:
|
||||
case 0x3000 ... 0x3fff:
|
||||
case 0x7000 ... 0x7fff:
|
||||
case 0x8300 ... 0x83ff:
|
||||
case 0x9c00 ... 0x9cff:
|
||||
case 0xb900 ... 0xb9ff:
|
||||
case 0x75201a:
|
||||
case 0x752045:
|
||||
case 0x752046:
|
||||
case 0x752048:
|
||||
case 0x75204a:
|
||||
case 0x75206b:
|
||||
case 0x752080:
|
||||
case 0x752081:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt700_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x2009:
|
||||
case 0x2016:
|
||||
case 0x201b:
|
||||
case 0x201c:
|
||||
case 0x201d:
|
||||
case 0x201f:
|
||||
case 0x2021:
|
||||
case 0x2023:
|
||||
case 0x2230:
|
||||
case 0x200b ... 0x200e: /* i2c read */
|
||||
case 0x2012 ... 0x2015: /* HD-A read */
|
||||
case 0x202d ... 0x202f: /* BRA */
|
||||
case 0x2201 ... 0x2212: /* i2c debug */
|
||||
case 0x2220 ... 0x2223: /* decoded HD-A */
|
||||
case 0x9c00 ... 0x9cff:
|
||||
case 0xb900 ... 0xb9ff:
|
||||
case 0xff01:
|
||||
case 0x75201a:
|
||||
case 0x752046:
|
||||
case 0x752080:
|
||||
case 0x752081:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(dev);
|
||||
unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
|
||||
unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
|
||||
unsigned int is_hda_reg = 1, is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT700_PRIV_DATA_R_H | nid;
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg3, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x3000) {
|
||||
reg += 0x8000;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
reg += 0x2000;
|
||||
reg |= 0x800;
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg2, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x9000) {
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0xb000) {
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regmap_read(rt700->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_hda_reg = 0;
|
||||
}
|
||||
|
||||
if (is_hda_reg || is_index_reg) {
|
||||
sdw_data_3 = 0;
|
||||
sdw_data_2 = 0;
|
||||
sdw_data_1 = 0;
|
||||
sdw_data_0 = 0;
|
||||
ret = regmap_read(rt700->sdw_regmap,
|
||||
RT700_READ_HDA_3, &sdw_data_3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt700->sdw_regmap,
|
||||
RT700_READ_HDA_2, &sdw_data_2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt700->sdw_regmap,
|
||||
RT700_READ_HDA_1, &sdw_data_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt700->sdw_regmap,
|
||||
RT700_READ_HDA_0, &sdw_data_0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ((sdw_data_3 & 0xff) << 24) |
|
||||
((sdw_data_2 & 0xff) << 16) |
|
||||
((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
|
||||
}
|
||||
|
||||
if (is_hda_reg == 0)
|
||||
dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
|
||||
__func__, reg, reg2, reg3, reg4, *val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x => %08x\n",
|
||||
__func__, reg, reg2, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(dev);
|
||||
unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
|
||||
unsigned int is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT700_PRIV_DATA_W_H | nid;
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg3, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_index_reg = 1;
|
||||
} else if (reg < 0x4fff) {
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (reg == 0xff01) {
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt700->sdw_regmap,
|
||||
reg2, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg2 == 0)
|
||||
dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
|
||||
__func__, reg, reg2, reg3, reg4, val2, val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
|
||||
__func__, reg, reg2, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config rt700_regmap = {
|
||||
.reg_bits = 24,
|
||||
.val_bits = 32,
|
||||
.readable_reg = rt700_readable_register,
|
||||
.volatile_reg = rt700_volatile_register,
|
||||
.max_register = 0x755800,
|
||||
.reg_defaults = rt700_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
.reg_read = rt700_sdw_read,
|
||||
.reg_write = rt700_sdw_write,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt700_sdw_regmap = {
|
||||
.name = "sdw",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 8,
|
||||
.readable_reg = rt700_readable_register,
|
||||
.max_register = 0xff01,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static int rt700_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
/* Update the status */
|
||||
rt700->status = status;
|
||||
|
||||
if (status == SDW_SLAVE_UNATTACHED)
|
||||
rt700->hw_init = false;
|
||||
|
||||
/*
|
||||
* Perform initialization only if slave status is present and
|
||||
* hw_init flag is false
|
||||
*/
|
||||
if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED)
|
||||
return 0;
|
||||
|
||||
/* perform I/O transfers required for Slave initialization */
|
||||
return rt700_io_init(&slave->dev, slave);
|
||||
}
|
||||
|
||||
static int rt700_read_prop(struct sdw_slave *slave)
|
||||
{
|
||||
struct sdw_slave_prop *prop = &slave->prop;
|
||||
int nval, i, num_of_ports = 1;
|
||||
u32 bit;
|
||||
unsigned long addr;
|
||||
struct sdw_dpn_prop *dpn;
|
||||
|
||||
prop->paging_support = false;
|
||||
|
||||
/* first we need to allocate memory for set bits in port lists */
|
||||
prop->source_ports = 0x14; /* BITMAP: 00010100 */
|
||||
prop->sink_ports = 0xA; /* BITMAP: 00001010 */
|
||||
|
||||
nval = hweight32(prop->source_ports);
|
||||
num_of_ports += nval;
|
||||
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->src_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->src_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
dpn = prop->src_dpn_prop;
|
||||
addr = prop->source_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].type = SDW_DPN_FULL;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* do this again for sink now */
|
||||
nval = hweight32(prop->sink_ports);
|
||||
num_of_ports += nval;
|
||||
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->sink_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->sink_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
dpn = prop->sink_dpn_prop;
|
||||
addr = prop->sink_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].type = SDW_DPN_FULL;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Allocate port_ready based on num_of_ports */
|
||||
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
|
||||
sizeof(*slave->port_ready),
|
||||
GFP_KERNEL);
|
||||
if (!slave->port_ready)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize completion */
|
||||
for (i = 0; i < num_of_ports; i++)
|
||||
init_completion(&slave->port_ready[i]);
|
||||
|
||||
/* set the timeout values */
|
||||
prop->clk_stop_timeout = 20;
|
||||
|
||||
/* wake-up event */
|
||||
prop->wake_capable = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt700_bus_config(struct sdw_slave *slave,
|
||||
struct sdw_bus_params *params)
|
||||
{
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
|
||||
int ret;
|
||||
|
||||
memcpy(&rt700->params, params, sizeof(*params));
|
||||
|
||||
ret = rt700_clock_config(&slave->dev);
|
||||
if (ret < 0)
|
||||
dev_err(&slave->dev, "Invalid clk config");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt700_interrupt_callback(struct sdw_slave *slave,
|
||||
struct sdw_slave_intr_status *status)
|
||||
{
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
dev_dbg(&slave->dev,
|
||||
"%s control_port_stat=%x", __func__, status->control_port);
|
||||
|
||||
if (status->control_port & 0x4) {
|
||||
mod_delayed_work(system_power_efficient_wq,
|
||||
&rt700->jack_detect_work, msecs_to_jiffies(250));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* slave_ops: callbacks for get_clock_stop_mode, clock_stop and
|
||||
* port_prep are not defined for now
|
||||
*/
|
||||
static struct sdw_slave_ops rt700_slave_ops = {
|
||||
.read_prop = rt700_read_prop,
|
||||
.interrupt_callback = rt700_interrupt_callback,
|
||||
.update_status = rt700_update_status,
|
||||
.bus_config = rt700_bus_config,
|
||||
};
|
||||
|
||||
static int rt700_sdw_probe(struct sdw_slave *slave,
|
||||
const struct sdw_device_id *id)
|
||||
{
|
||||
struct regmap *sdw_regmap, *regmap;
|
||||
|
||||
/* Assign ops */
|
||||
slave->ops = &rt700_slave_ops;
|
||||
|
||||
/* Regmap Initialization */
|
||||
sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap);
|
||||
if (!sdw_regmap)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init(&slave->dev, NULL,
|
||||
&slave->dev, &rt700_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
rt700_init(&slave->dev, sdw_regmap, regmap, slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt700_sdw_remove(struct sdw_slave *slave)
|
||||
{
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
if (rt700 && rt700->hw_init) {
|
||||
cancel_delayed_work(&rt700->jack_detect_work);
|
||||
cancel_delayed_work(&rt700->jack_btn_check_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdw_device_id rt700_id[] = {
|
||||
SDW_SLAVE_ENTRY(0x025d, 0x700, 0),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdw, rt700_id);
|
||||
|
||||
static int rt700_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(dev);
|
||||
|
||||
if (!rt700->hw_init)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(rt700->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT700_PROBE_TIMEOUT 2000
|
||||
|
||||
static int rt700_dev_resume(struct device *dev)
|
||||
{
|
||||
struct sdw_slave *slave = dev_to_sdw_dev(dev);
|
||||
struct rt700_priv *rt700 = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
if (!rt700->hw_init)
|
||||
return 0;
|
||||
|
||||
if (!slave->unattach_request)
|
||||
goto regmap_sync;
|
||||
|
||||
time = wait_for_completion_timeout(&slave->initialization_complete,
|
||||
msecs_to_jiffies(RT700_PROBE_TIMEOUT));
|
||||
if (!time) {
|
||||
dev_err(&slave->dev, "Initialization not complete, timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_sync:
|
||||
slave->unattach_request = 0;
|
||||
regcache_cache_only(rt700->regmap, false);
|
||||
regcache_sync_region(rt700->regmap, 0x3000, 0x8fff);
|
||||
regcache_sync_region(rt700->regmap, 0x752010, 0x75206b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rt700_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_suspend, rt700_dev_resume)
|
||||
SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL)
|
||||
};
|
||||
|
||||
static struct sdw_driver rt700_sdw_driver = {
|
||||
.driver = {
|
||||
.name = "rt700",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rt700_pm,
|
||||
},
|
||||
.probe = rt700_sdw_probe,
|
||||
.remove = rt700_sdw_remove,
|
||||
.ops = &rt700_slave_ops,
|
||||
.id_table = rt700_id,
|
||||
};
|
||||
module_sdw_driver(rt700_sdw_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT700 driver SDW");
|
||||
MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
335
sound/soc/codecs/rt700-sdw.h
Normal file
335
sound/soc/codecs/rt700-sdw.h
Normal file
@ -0,0 +1,335 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt700-sdw.h -- RT700 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT700_SDW_H__
|
||||
#define __RT700_SDW_H__
|
||||
|
||||
static const struct reg_default rt700_reg_defaults[] = {
|
||||
{ 0x0000, 0x0000 },
|
||||
{ 0x0001, 0x0000 },
|
||||
{ 0x0002, 0x0000 },
|
||||
{ 0x0003, 0x0000 },
|
||||
{ 0x0004, 0x0000 },
|
||||
{ 0x0005, 0x0001 },
|
||||
{ 0x0020, 0x0000 },
|
||||
{ 0x0022, 0x0000 },
|
||||
{ 0x0023, 0x0000 },
|
||||
{ 0x0024, 0x0000 },
|
||||
{ 0x0025, 0x0000 },
|
||||
{ 0x0026, 0x0000 },
|
||||
{ 0x0030, 0x0000 },
|
||||
{ 0x0032, 0x0000 },
|
||||
{ 0x0033, 0x0000 },
|
||||
{ 0x0034, 0x0000 },
|
||||
{ 0x0035, 0x0000 },
|
||||
{ 0x0036, 0x0000 },
|
||||
{ 0x0040, 0x0000 },
|
||||
{ 0x0041, 0x0000 },
|
||||
{ 0x0042, 0x0000 },
|
||||
{ 0x0043, 0x0000 },
|
||||
{ 0x0044, 0x0020 },
|
||||
{ 0x0045, 0x0001 },
|
||||
{ 0x0046, 0x0000 },
|
||||
{ 0x0050, 0x0000 },
|
||||
{ 0x0051, 0x0000 },
|
||||
{ 0x0052, 0x0000 },
|
||||
{ 0x0053, 0x0000 },
|
||||
{ 0x0054, 0x0000 },
|
||||
{ 0x0055, 0x0000 },
|
||||
{ 0x0060, 0x0000 },
|
||||
{ 0x0070, 0x0000 },
|
||||
{ 0x00e0, 0x0000 },
|
||||
{ 0x00f0, 0x0000 },
|
||||
{ 0x0100, 0x0000 },
|
||||
{ 0x0101, 0x0000 },
|
||||
{ 0x0102, 0x0000 },
|
||||
{ 0x0103, 0x0000 },
|
||||
{ 0x0104, 0x0000 },
|
||||
{ 0x0105, 0x0000 },
|
||||
{ 0x0120, 0x0000 },
|
||||
{ 0x0121, 0x0000 },
|
||||
{ 0x0122, 0x0000 },
|
||||
{ 0x0123, 0x0000 },
|
||||
{ 0x0124, 0x0000 },
|
||||
{ 0x0125, 0x0000 },
|
||||
{ 0x0126, 0x0000 },
|
||||
{ 0x0127, 0x0000 },
|
||||
{ 0x0130, 0x0000 },
|
||||
{ 0x0131, 0x0000 },
|
||||
{ 0x0132, 0x0000 },
|
||||
{ 0x0133, 0x0000 },
|
||||
{ 0x0134, 0x0000 },
|
||||
{ 0x0135, 0x0000 },
|
||||
{ 0x0136, 0x0000 },
|
||||
{ 0x0137, 0x0000 },
|
||||
{ 0x0200, 0x0000 },
|
||||
{ 0x0201, 0x0000 },
|
||||
{ 0x0202, 0x0000 },
|
||||
{ 0x0203, 0x0000 },
|
||||
{ 0x0204, 0x0000 },
|
||||
{ 0x0205, 0x0000 },
|
||||
{ 0x0220, 0x0000 },
|
||||
{ 0x0221, 0x0000 },
|
||||
{ 0x0222, 0x0000 },
|
||||
{ 0x0223, 0x0000 },
|
||||
{ 0x0224, 0x0000 },
|
||||
{ 0x0225, 0x0000 },
|
||||
{ 0x0226, 0x0000 },
|
||||
{ 0x0227, 0x0000 },
|
||||
{ 0x0230, 0x0000 },
|
||||
{ 0x0231, 0x0000 },
|
||||
{ 0x0232, 0x0000 },
|
||||
{ 0x0233, 0x0000 },
|
||||
{ 0x0234, 0x0000 },
|
||||
{ 0x0235, 0x0000 },
|
||||
{ 0x0236, 0x0000 },
|
||||
{ 0x0237, 0x0000 },
|
||||
{ 0x0300, 0x0000 },
|
||||
{ 0x0301, 0x0000 },
|
||||
{ 0x0302, 0x0000 },
|
||||
{ 0x0303, 0x0000 },
|
||||
{ 0x0304, 0x0000 },
|
||||
{ 0x0305, 0x0000 },
|
||||
{ 0x0320, 0x0000 },
|
||||
{ 0x0321, 0x0000 },
|
||||
{ 0x0322, 0x0000 },
|
||||
{ 0x0323, 0x0000 },
|
||||
{ 0x0324, 0x0000 },
|
||||
{ 0x0325, 0x0000 },
|
||||
{ 0x0326, 0x0000 },
|
||||
{ 0x0327, 0x0000 },
|
||||
{ 0x0330, 0x0000 },
|
||||
{ 0x0331, 0x0000 },
|
||||
{ 0x0332, 0x0000 },
|
||||
{ 0x0333, 0x0000 },
|
||||
{ 0x0334, 0x0000 },
|
||||
{ 0x0335, 0x0000 },
|
||||
{ 0x0336, 0x0000 },
|
||||
{ 0x0337, 0x0000 },
|
||||
{ 0x0400, 0x0000 },
|
||||
{ 0x0401, 0x0000 },
|
||||
{ 0x0402, 0x0000 },
|
||||
{ 0x0403, 0x0000 },
|
||||
{ 0x0404, 0x0000 },
|
||||
{ 0x0405, 0x0000 },
|
||||
{ 0x0420, 0x0000 },
|
||||
{ 0x0421, 0x0000 },
|
||||
{ 0x0422, 0x0000 },
|
||||
{ 0x0423, 0x0000 },
|
||||
{ 0x0424, 0x0000 },
|
||||
{ 0x0425, 0x0000 },
|
||||
{ 0x0426, 0x0000 },
|
||||
{ 0x0427, 0x0000 },
|
||||
{ 0x0430, 0x0000 },
|
||||
{ 0x0431, 0x0000 },
|
||||
{ 0x0432, 0x0000 },
|
||||
{ 0x0433, 0x0000 },
|
||||
{ 0x0434, 0x0000 },
|
||||
{ 0x0435, 0x0000 },
|
||||
{ 0x0436, 0x0000 },
|
||||
{ 0x0437, 0x0000 },
|
||||
{ 0x0500, 0x0000 },
|
||||
{ 0x0501, 0x0000 },
|
||||
{ 0x0502, 0x0000 },
|
||||
{ 0x0503, 0x0000 },
|
||||
{ 0x0504, 0x0000 },
|
||||
{ 0x0505, 0x0000 },
|
||||
{ 0x0520, 0x0000 },
|
||||
{ 0x0521, 0x0000 },
|
||||
{ 0x0522, 0x0000 },
|
||||
{ 0x0523, 0x0000 },
|
||||
{ 0x0524, 0x0000 },
|
||||
{ 0x0525, 0x0000 },
|
||||
{ 0x0526, 0x0000 },
|
||||
{ 0x0527, 0x0000 },
|
||||
{ 0x0530, 0x0000 },
|
||||
{ 0x0531, 0x0000 },
|
||||
{ 0x0532, 0x0000 },
|
||||
{ 0x0533, 0x0000 },
|
||||
{ 0x0534, 0x0000 },
|
||||
{ 0x0535, 0x0000 },
|
||||
{ 0x0536, 0x0000 },
|
||||
{ 0x0537, 0x0000 },
|
||||
{ 0x0600, 0x0000 },
|
||||
{ 0x0601, 0x0000 },
|
||||
{ 0x0602, 0x0000 },
|
||||
{ 0x0603, 0x0000 },
|
||||
{ 0x0604, 0x0000 },
|
||||
{ 0x0605, 0x0000 },
|
||||
{ 0x0620, 0x0000 },
|
||||
{ 0x0621, 0x0000 },
|
||||
{ 0x0622, 0x0000 },
|
||||
{ 0x0623, 0x0000 },
|
||||
{ 0x0624, 0x0000 },
|
||||
{ 0x0625, 0x0000 },
|
||||
{ 0x0626, 0x0000 },
|
||||
{ 0x0627, 0x0000 },
|
||||
{ 0x0630, 0x0000 },
|
||||
{ 0x0631, 0x0000 },
|
||||
{ 0x0632, 0x0000 },
|
||||
{ 0x0633, 0x0000 },
|
||||
{ 0x0634, 0x0000 },
|
||||
{ 0x0635, 0x0000 },
|
||||
{ 0x0636, 0x0000 },
|
||||
{ 0x0637, 0x0000 },
|
||||
{ 0x0700, 0x0000 },
|
||||
{ 0x0701, 0x0000 },
|
||||
{ 0x0702, 0x0000 },
|
||||
{ 0x0703, 0x0000 },
|
||||
{ 0x0704, 0x0000 },
|
||||
{ 0x0705, 0x0000 },
|
||||
{ 0x0720, 0x0000 },
|
||||
{ 0x0721, 0x0000 },
|
||||
{ 0x0722, 0x0000 },
|
||||
{ 0x0723, 0x0000 },
|
||||
{ 0x0724, 0x0000 },
|
||||
{ 0x0725, 0x0000 },
|
||||
{ 0x0726, 0x0000 },
|
||||
{ 0x0727, 0x0000 },
|
||||
{ 0x0730, 0x0000 },
|
||||
{ 0x0731, 0x0000 },
|
||||
{ 0x0732, 0x0000 },
|
||||
{ 0x0733, 0x0000 },
|
||||
{ 0x0734, 0x0000 },
|
||||
{ 0x0735, 0x0000 },
|
||||
{ 0x0736, 0x0000 },
|
||||
{ 0x0737, 0x0000 },
|
||||
{ 0x0800, 0x0000 },
|
||||
{ 0x0801, 0x0000 },
|
||||
{ 0x0802, 0x0000 },
|
||||
{ 0x0803, 0x0000 },
|
||||
{ 0x0804, 0x0000 },
|
||||
{ 0x0805, 0x0000 },
|
||||
{ 0x0820, 0x0000 },
|
||||
{ 0x0821, 0x0000 },
|
||||
{ 0x0822, 0x0000 },
|
||||
{ 0x0823, 0x0000 },
|
||||
{ 0x0824, 0x0000 },
|
||||
{ 0x0825, 0x0000 },
|
||||
{ 0x0826, 0x0000 },
|
||||
{ 0x0827, 0x0000 },
|
||||
{ 0x0830, 0x0000 },
|
||||
{ 0x0831, 0x0000 },
|
||||
{ 0x0832, 0x0000 },
|
||||
{ 0x0833, 0x0000 },
|
||||
{ 0x0834, 0x0000 },
|
||||
{ 0x0835, 0x0000 },
|
||||
{ 0x0836, 0x0000 },
|
||||
{ 0x0837, 0x0000 },
|
||||
{ 0x0f00, 0x0000 },
|
||||
{ 0x0f01, 0x0000 },
|
||||
{ 0x0f02, 0x0000 },
|
||||
{ 0x0f03, 0x0000 },
|
||||
{ 0x0f04, 0x0000 },
|
||||
{ 0x0f05, 0x0000 },
|
||||
{ 0x0f20, 0x0000 },
|
||||
{ 0x0f21, 0x0000 },
|
||||
{ 0x0f22, 0x0000 },
|
||||
{ 0x0f23, 0x0000 },
|
||||
{ 0x0f24, 0x0000 },
|
||||
{ 0x0f25, 0x0000 },
|
||||
{ 0x0f26, 0x0000 },
|
||||
{ 0x0f27, 0x0000 },
|
||||
{ 0x0f30, 0x0000 },
|
||||
{ 0x0f31, 0x0000 },
|
||||
{ 0x0f32, 0x0000 },
|
||||
{ 0x0f33, 0x0000 },
|
||||
{ 0x0f34, 0x0000 },
|
||||
{ 0x0f35, 0x0000 },
|
||||
{ 0x0f36, 0x0000 },
|
||||
{ 0x0f37, 0x0000 },
|
||||
{ 0x2000, 0x0000 },
|
||||
{ 0x2001, 0x0000 },
|
||||
{ 0x2002, 0x0000 },
|
||||
{ 0x2003, 0x0000 },
|
||||
{ 0x2004, 0x0000 },
|
||||
{ 0x2005, 0x0000 },
|
||||
{ 0x2006, 0x0000 },
|
||||
{ 0x2007, 0x0000 },
|
||||
{ 0x2008, 0x0000 },
|
||||
{ 0x2009, 0x0003 },
|
||||
{ 0x200a, 0x0003 },
|
||||
{ 0x200b, 0x0000 },
|
||||
{ 0x200c, 0x0000 },
|
||||
{ 0x200d, 0x0000 },
|
||||
{ 0x200e, 0x0000 },
|
||||
{ 0x2012, 0x0000 },
|
||||
{ 0x2013, 0x0000 },
|
||||
{ 0x2014, 0x0000 },
|
||||
{ 0x2015, 0x0000 },
|
||||
{ 0x2016, 0x0000 },
|
||||
{ 0x201a, 0x0000 },
|
||||
{ 0x201b, 0x0000 },
|
||||
{ 0x201c, 0x0000 },
|
||||
{ 0x201d, 0x0000 },
|
||||
{ 0x201e, 0x0000 },
|
||||
{ 0x201f, 0x0000 },
|
||||
{ 0x2020, 0x0000 },
|
||||
{ 0x2021, 0x0000 },
|
||||
{ 0x2022, 0x0000 },
|
||||
{ 0x2023, 0x0000 },
|
||||
{ 0x2024, 0x0000 },
|
||||
{ 0x2025, 0x0002 },
|
||||
{ 0x2026, 0x0000 },
|
||||
{ 0x2027, 0x0000 },
|
||||
{ 0x2029, 0x0000 },
|
||||
{ 0x202a, 0x0000 },
|
||||
{ 0x202d, 0x0000 },
|
||||
{ 0x202e, 0x0000 },
|
||||
{ 0x202f, 0x0000 },
|
||||
{ 0x2030, 0x0000 },
|
||||
{ 0x2031, 0x0000 },
|
||||
{ 0x2032, 0x0000 },
|
||||
{ 0x2033, 0x0000 },
|
||||
{ 0x2034, 0x0000 },
|
||||
{ 0x2200, 0x0000 },
|
||||
{ 0x2201, 0x0000 },
|
||||
{ 0x2202, 0x0000 },
|
||||
{ 0x2203, 0x0000 },
|
||||
{ 0x2204, 0x0000 },
|
||||
{ 0x2206, 0x0000 },
|
||||
{ 0x2207, 0x0000 },
|
||||
{ 0x2208, 0x0000 },
|
||||
{ 0x2209, 0x0000 },
|
||||
{ 0x220a, 0x0000 },
|
||||
{ 0x220b, 0x0000 },
|
||||
{ 0x220c, 0x0000 },
|
||||
{ 0x220d, 0x0000 },
|
||||
{ 0x220e, 0x0000 },
|
||||
{ 0x220f, 0x0000 },
|
||||
{ 0x2211, 0x0000 },
|
||||
{ 0x2212, 0x0000 },
|
||||
{ 0x2220, 0x0000 },
|
||||
{ 0x2221, 0x0000 },
|
||||
{ 0x2222, 0x0000 },
|
||||
{ 0x2223, 0x0000 },
|
||||
{ 0x2230, 0x0000 },
|
||||
{ 0x2231, 0x0000 },
|
||||
{ 0x3121, 0x0001 },
|
||||
{ 0x3122, 0x0000 },
|
||||
{ 0x3123, 0x0000 },
|
||||
{ 0x7303, 0x0057 },
|
||||
{ 0x7303, 0x0057 },
|
||||
{ 0x8383, 0x0057 },
|
||||
{ 0x7308, 0x0097 },
|
||||
{ 0x8388, 0x0097 },
|
||||
{ 0x7309, 0x0097 },
|
||||
{ 0x8389, 0x0097 },
|
||||
{ 0x7312, 0x0000 },
|
||||
{ 0x8392, 0x0000 },
|
||||
{ 0x7313, 0x0000 },
|
||||
{ 0x8393, 0x0000 },
|
||||
{ 0x7319, 0x0000 },
|
||||
{ 0x8399, 0x0000 },
|
||||
{ 0x75201a, 0x8003 },
|
||||
{ 0x752045, 0x5289 },
|
||||
{ 0x752048, 0xd049 },
|
||||
{ 0x75204a, 0xa83b },
|
||||
{ 0x75206b, 0x5064 },
|
||||
};
|
||||
|
||||
#endif /* __RT700_H__ */
|
1237
sound/soc/codecs/rt700.c
Normal file
1237
sound/soc/codecs/rt700.c
Normal file
File diff suppressed because it is too large
Load Diff
174
sound/soc/codecs/rt700.h
Normal file
174
sound/soc/codecs/rt700.h
Normal file
@ -0,0 +1,174 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt700.h -- RT700 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT700_H__
|
||||
#define __RT700_H__
|
||||
|
||||
extern const struct dev_pm_ops rt700_runtime_pm;
|
||||
|
||||
struct rt700_priv {
|
||||
struct snd_soc_component *component;
|
||||
struct regmap *regmap;
|
||||
struct regmap *sdw_regmap;
|
||||
struct sdw_slave *slave;
|
||||
enum sdw_slave_status status;
|
||||
struct sdw_bus_params params;
|
||||
bool hw_init;
|
||||
bool first_hw_init;
|
||||
struct snd_soc_jack *hs_jack;
|
||||
struct delayed_work jack_detect_work;
|
||||
struct delayed_work jack_btn_check_work;
|
||||
int jack_type;
|
||||
};
|
||||
|
||||
struct sdw_stream_data {
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
};
|
||||
|
||||
/* NID */
|
||||
#define RT700_AUDIO_FUNCTION_GROUP 0x01
|
||||
#define RT700_DAC_OUT1 0x02
|
||||
#define RT700_DAC_OUT2 0x03
|
||||
#define RT700_ADC_IN1 0x09
|
||||
#define RT700_ADC_IN2 0x08
|
||||
#define RT700_DMIC1 0x12
|
||||
#define RT700_DMIC2 0x13
|
||||
#define RT700_SPK_OUT 0x14
|
||||
#define RT700_MIC2 0x19
|
||||
#define RT700_LINE1 0x1a
|
||||
#define RT700_LINE2 0x1b
|
||||
#define RT700_BEEP 0x1d
|
||||
#define RT700_SPDIF 0x1e
|
||||
#define RT700_VENDOR_REGISTERS 0x20
|
||||
#define RT700_HP_OUT 0x21
|
||||
#define RT700_MIXER_IN1 0x22
|
||||
#define RT700_MIXER_IN2 0x23
|
||||
#define RT700_INLINE_CMD 0x55
|
||||
|
||||
/* Index (NID:20h) */
|
||||
#define RT700_DAC_DC_CALI_CTL1 0x00
|
||||
#define RT700_PARA_VERB_CTL 0x1a
|
||||
#define RT700_COMBO_JACK_AUTO_CTL1 0x45
|
||||
#define RT700_COMBO_JACK_AUTO_CTL2 0x46
|
||||
#define RT700_INLINE_CMD_CTL 0x48
|
||||
#define RT700_DIGITAL_MISC_CTRL4 0x4a
|
||||
#define RT700_VREFOUT_CTL 0x6b
|
||||
#define RT700_FSM_CTL 0x6f
|
||||
#define RT700_IRQ_FLAG_TABLE1 0x80
|
||||
#define RT700_IRQ_FLAG_TABLE2 0x81
|
||||
#define RT700_IRQ_FLAG_TABLE3 0x82
|
||||
|
||||
/* Verb */
|
||||
#define RT700_VERB_SET_CONNECT_SEL 0x3100
|
||||
#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00
|
||||
#define RT700_VERB_GET_CONNECT_SEL 0xb100
|
||||
#define RT700_VERB_SET_POWER_STATE 0x3500
|
||||
#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600
|
||||
#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700
|
||||
#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800
|
||||
#define RT700_SET_AMP_GAIN_MUTE_H 0x7300
|
||||
#define RT700_SET_AMP_GAIN_MUTE_L 0x8380
|
||||
#define RT700_VERB_GET_PIN_SENSE 0xb900
|
||||
|
||||
#define RT700_READ_HDA_3 0x2012
|
||||
#define RT700_READ_HDA_2 0x2013
|
||||
#define RT700_READ_HDA_1 0x2014
|
||||
#define RT700_READ_HDA_0 0x2015
|
||||
#define RT700_PRIV_INDEX_W_H 0x7520
|
||||
#define RT700_PRIV_INDEX_W_L 0x85a0
|
||||
#define RT700_PRIV_DATA_W_H 0x7420
|
||||
#define RT700_PRIV_DATA_W_L 0x84a0
|
||||
#define RT700_PRIV_INDEX_R_H 0x9d20
|
||||
#define RT700_PRIV_INDEX_R_L 0xada0
|
||||
#define RT700_PRIV_DATA_R_H 0x9c20
|
||||
#define RT700_PRIV_DATA_R_L 0xaca0
|
||||
#define RT700_DAC_FORMAT_H 0x7203
|
||||
#define RT700_DAC_FORMAT_L 0x8283
|
||||
#define RT700_ADC_FORMAT_H 0x7209
|
||||
#define RT700_ADC_FORMAT_L 0x8289
|
||||
#define RT700_SET_AUDIO_POWER_STATE\
|
||||
(RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP)
|
||||
#define RT700_SET_PIN_DMIC1\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1)
|
||||
#define RT700_SET_PIN_DMIC2\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2)
|
||||
#define RT700_SET_PIN_SPK\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT)
|
||||
#define RT700_SET_PIN_HP\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT)
|
||||
#define RT700_SET_PIN_MIC2\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2)
|
||||
#define RT700_SET_PIN_LINE1\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1)
|
||||
#define RT700_SET_PIN_LINE2\
|
||||
(RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2)
|
||||
#define RT700_SET_MIC2_UNSOLICITED_ENABLE\
|
||||
(RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2)
|
||||
#define RT700_SET_HP_UNSOLICITED_ENABLE\
|
||||
(RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT)
|
||||
#define RT700_SET_INLINE_UNSOLICITED_ENABLE\
|
||||
(RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD)
|
||||
#define RT700_SET_STREAMID_DAC1\
|
||||
(RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1)
|
||||
#define RT700_SET_STREAMID_DAC2\
|
||||
(RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2)
|
||||
#define RT700_SET_STREAMID_ADC1\
|
||||
(RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1)
|
||||
#define RT700_SET_STREAMID_ADC2\
|
||||
(RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2)
|
||||
#define RT700_SET_GAIN_DAC1_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1)
|
||||
#define RT700_SET_GAIN_DAC1_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1)
|
||||
#define RT700_SET_GAIN_ADC1_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1)
|
||||
#define RT700_SET_GAIN_ADC1_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1)
|
||||
#define RT700_SET_GAIN_ADC2_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2)
|
||||
#define RT700_SET_GAIN_ADC2_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2)
|
||||
#define RT700_SET_GAIN_AMIC_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2)
|
||||
#define RT700_SET_GAIN_AMIC_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2)
|
||||
#define RT700_SET_GAIN_HP_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT)
|
||||
#define RT700_SET_GAIN_HP_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT)
|
||||
#define RT700_SET_GAIN_SPK_L\
|
||||
(RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT)
|
||||
#define RT700_SET_GAIN_SPK_H\
|
||||
(RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT)
|
||||
#define RT700_SET_EAPD_SPK\
|
||||
(RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT)
|
||||
|
||||
/* combo jack auto switch control 2 (0x46)(NID:20h) */
|
||||
#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
|
||||
#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
|
||||
#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
|
||||
#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
|
||||
|
||||
#define RT700_EAPD_HIGH 0x2
|
||||
#define RT700_EAPD_LOW 0x0
|
||||
#define RT700_MUTE_SFT 7
|
||||
#define RT700_DIR_IN_SFT 6
|
||||
#define RT700_DIR_OUT_SFT 7
|
||||
|
||||
enum {
|
||||
RT700_AIF1,
|
||||
RT700_AIF2,
|
||||
RT700_AIFS,
|
||||
};
|
||||
|
||||
int rt700_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt700_init(struct device *dev, struct regmap *sdw_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave);
|
||||
|
||||
int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic);
|
||||
int rt700_clock_config(struct device *dev);
|
||||
#endif /* __RT700_H__ */
|
552
sound/soc/codecs/rt711-sdw.c
Normal file
552
sound/soc/codecs/rt711-sdw.c
Normal file
@ -0,0 +1,552 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// rt711-sdw.c -- rt711 ALSA SoC audio driver
|
||||
//
|
||||
// Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
//
|
||||
//
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include "rt711.h"
|
||||
#include "rt711-sdw.h"
|
||||
|
||||
static bool rt711_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00e0:
|
||||
case 0x00f0:
|
||||
case 0x2012 ... 0x2016:
|
||||
case 0x201a ... 0x2027:
|
||||
case 0x2029 ... 0x202a:
|
||||
case 0x202d ... 0x2034:
|
||||
case 0x2201 ... 0x2204:
|
||||
case 0x2206 ... 0x2212:
|
||||
case 0x2220 ... 0x2223:
|
||||
case 0x2230 ... 0x2239:
|
||||
case 0x2f01 ... 0x2f0f:
|
||||
case 0x3000 ... 0x3fff:
|
||||
case 0x7000 ... 0x7fff:
|
||||
case 0x8300 ... 0x83ff:
|
||||
case 0x9c00 ... 0x9cff:
|
||||
case 0xb900 ... 0xb9ff:
|
||||
case 0x752009:
|
||||
case 0x752011:
|
||||
case 0x75201a:
|
||||
case 0x752045:
|
||||
case 0x752046:
|
||||
case 0x752048:
|
||||
case 0x75204a:
|
||||
case 0x75206b:
|
||||
case 0x75206f:
|
||||
case 0x752080:
|
||||
case 0x752081:
|
||||
case 0x752091:
|
||||
case 0x755800:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt711_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x2016:
|
||||
case 0x201b:
|
||||
case 0x201c:
|
||||
case 0x201d:
|
||||
case 0x201f:
|
||||
case 0x2021:
|
||||
case 0x2023:
|
||||
case 0x2230:
|
||||
case 0x2012 ... 0x2015: /* HD-A read */
|
||||
case 0x202d ... 0x202f: /* BRA */
|
||||
case 0x2201 ... 0x2212: /* i2c debug */
|
||||
case 0x2220 ... 0x2223: /* decoded HD-A */
|
||||
case 0x9c00 ... 0x9cff:
|
||||
case 0xb900 ... 0xb9ff:
|
||||
case 0xff01:
|
||||
case 0x75201a:
|
||||
case 0x752046:
|
||||
case 0x752080:
|
||||
case 0x752081:
|
||||
case 0x755800:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(dev);
|
||||
unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
|
||||
unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
|
||||
unsigned int is_hda_reg = 1, is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT711_PRIV_DATA_R_H | nid;
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg3, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x3000) {
|
||||
reg += 0x8000;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
reg += 0x2000;
|
||||
reg |= 0x800;
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg2, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x9000) {
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg, ((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0xb000) {
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regmap_read(rt711->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_hda_reg = 0;
|
||||
}
|
||||
|
||||
if (is_hda_reg || is_index_reg) {
|
||||
sdw_data_3 = 0;
|
||||
sdw_data_2 = 0;
|
||||
sdw_data_1 = 0;
|
||||
sdw_data_0 = 0;
|
||||
ret = regmap_read(rt711->sdw_regmap,
|
||||
RT711_READ_HDA_3, &sdw_data_3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt711->sdw_regmap,
|
||||
RT711_READ_HDA_2, &sdw_data_2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt711->sdw_regmap,
|
||||
RT711_READ_HDA_1, &sdw_data_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt711->sdw_regmap,
|
||||
RT711_READ_HDA_0, &sdw_data_0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ((sdw_data_3 & 0xff) << 24) |
|
||||
((sdw_data_2 & 0xff) << 16) |
|
||||
((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
|
||||
}
|
||||
|
||||
if (is_hda_reg == 0)
|
||||
dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
|
||||
__func__, reg, reg2, reg3, reg4, *val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x => %08x\n",
|
||||
__func__, reg, reg2, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(dev);
|
||||
unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
|
||||
unsigned int is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT711_PRIV_DATA_W_H | nid;
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg3, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_index_reg = 1;
|
||||
} else if (reg < 0x4fff) {
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (reg == RT711_FUNC_RESET) {
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt711->sdw_regmap,
|
||||
reg2, ((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg2 == 0)
|
||||
dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
|
||||
__func__, reg, reg2, reg3, reg4, val2, val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
|
||||
__func__, reg, reg2, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config rt711_regmap = {
|
||||
.reg_bits = 24,
|
||||
.val_bits = 32,
|
||||
.readable_reg = rt711_readable_register,
|
||||
.volatile_reg = rt711_volatile_register,
|
||||
.max_register = 0x755800,
|
||||
.reg_defaults = rt711_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
.reg_read = rt711_sdw_read,
|
||||
.reg_write = rt711_sdw_write,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt711_sdw_regmap = {
|
||||
.name = "sdw",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 8,
|
||||
.readable_reg = rt711_readable_register,
|
||||
.max_register = 0xff01,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static int rt711_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
/* Update the status */
|
||||
rt711->status = status;
|
||||
|
||||
if (status == SDW_SLAVE_UNATTACHED)
|
||||
rt711->hw_init = false;
|
||||
|
||||
/*
|
||||
* Perform initialization only if slave status is present and
|
||||
* hw_init flag is false
|
||||
*/
|
||||
if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED)
|
||||
return 0;
|
||||
|
||||
/* perform I/O transfers required for Slave initialization */
|
||||
return rt711_io_init(&slave->dev, slave);
|
||||
}
|
||||
|
||||
static int rt711_read_prop(struct sdw_slave *slave)
|
||||
{
|
||||
struct sdw_slave_prop *prop = &slave->prop;
|
||||
int nval, i, num_of_ports = 1;
|
||||
u32 bit;
|
||||
unsigned long addr;
|
||||
struct sdw_dpn_prop *dpn;
|
||||
|
||||
prop->paging_support = false;
|
||||
|
||||
/* first we need to allocate memory for set bits in port lists */
|
||||
prop->source_ports = 0x14; /* BITMAP: 00010100 */
|
||||
prop->sink_ports = 0x8; /* BITMAP: 00001000 */
|
||||
|
||||
nval = hweight32(prop->source_ports);
|
||||
num_of_ports += nval;
|
||||
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->src_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->src_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
dpn = prop->src_dpn_prop;
|
||||
addr = prop->source_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].type = SDW_DPN_FULL;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* do this again for sink now */
|
||||
nval = hweight32(prop->sink_ports);
|
||||
num_of_ports += nval;
|
||||
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->sink_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->sink_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
dpn = prop->sink_dpn_prop;
|
||||
addr = prop->sink_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].type = SDW_DPN_FULL;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Allocate port_ready based on num_of_ports */
|
||||
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
|
||||
sizeof(*slave->port_ready),
|
||||
GFP_KERNEL);
|
||||
if (!slave->port_ready)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize completion */
|
||||
for (i = 0; i < num_of_ports; i++)
|
||||
init_completion(&slave->port_ready[i]);
|
||||
|
||||
/* set the timeout values */
|
||||
prop->clk_stop_timeout = 20;
|
||||
|
||||
/* wake-up event */
|
||||
prop->wake_capable = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt711_bus_config(struct sdw_slave *slave,
|
||||
struct sdw_bus_params *params)
|
||||
{
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
|
||||
int ret;
|
||||
|
||||
memcpy(&rt711->params, params, sizeof(*params));
|
||||
|
||||
ret = rt711_clock_config(&slave->dev);
|
||||
if (ret < 0)
|
||||
dev_err(&slave->dev, "Invalid clk config");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt711_interrupt_callback(struct sdw_slave *slave,
|
||||
struct sdw_slave_intr_status *status)
|
||||
{
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
dev_dbg(&slave->dev,
|
||||
"%s control_port_stat=%x", __func__, status->control_port);
|
||||
|
||||
if (status->control_port & 0x4) {
|
||||
mod_delayed_work(system_power_efficient_wq,
|
||||
&rt711->jack_detect_work, msecs_to_jiffies(250));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdw_slave_ops rt711_slave_ops = {
|
||||
.read_prop = rt711_read_prop,
|
||||
.interrupt_callback = rt711_interrupt_callback,
|
||||
.update_status = rt711_update_status,
|
||||
.bus_config = rt711_bus_config,
|
||||
};
|
||||
|
||||
static int rt711_sdw_probe(struct sdw_slave *slave,
|
||||
const struct sdw_device_id *id)
|
||||
{
|
||||
struct regmap *sdw_regmap, *regmap;
|
||||
|
||||
/* Assign ops */
|
||||
slave->ops = &rt711_slave_ops;
|
||||
|
||||
/* Regmap Initialization */
|
||||
sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap);
|
||||
if (!sdw_regmap)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init(&slave->dev, NULL,
|
||||
&slave->dev, &rt711_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
rt711_init(&slave->dev, sdw_regmap, regmap, slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt711_sdw_remove(struct sdw_slave *slave)
|
||||
{
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
if (rt711 && rt711->hw_init) {
|
||||
cancel_delayed_work(&rt711->jack_detect_work);
|
||||
cancel_delayed_work(&rt711->jack_btn_check_work);
|
||||
cancel_work_sync(&rt711->calibration_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdw_device_id rt711_id[] = {
|
||||
SDW_SLAVE_ENTRY(0x025d, 0x711, 0),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdw, rt711_id);
|
||||
|
||||
static int rt711_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(dev);
|
||||
|
||||
if (!rt711->hw_init)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(rt711->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT711_PROBE_TIMEOUT 2000
|
||||
|
||||
static int rt711_dev_resume(struct device *dev)
|
||||
{
|
||||
struct sdw_slave *slave = dev_to_sdw_dev(dev);
|
||||
struct rt711_priv *rt711 = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
if (!rt711->hw_init)
|
||||
return 0;
|
||||
|
||||
if (!slave->unattach_request)
|
||||
goto regmap_sync;
|
||||
|
||||
time = wait_for_completion_timeout(&slave->initialization_complete,
|
||||
msecs_to_jiffies(RT711_PROBE_TIMEOUT));
|
||||
if (!time) {
|
||||
dev_err(&slave->dev, "Initialization not complete, timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_sync:
|
||||
slave->unattach_request = 0;
|
||||
regcache_cache_only(rt711->regmap, false);
|
||||
regcache_sync_region(rt711->regmap, 0x3000, 0x8fff);
|
||||
regcache_sync_region(rt711->regmap, 0x752009, 0x752091);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rt711_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_suspend, rt711_dev_resume)
|
||||
SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL)
|
||||
};
|
||||
|
||||
static struct sdw_driver rt711_sdw_driver = {
|
||||
.driver = {
|
||||
.name = "rt711",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rt711_pm,
|
||||
},
|
||||
.probe = rt711_sdw_probe,
|
||||
.remove = rt711_sdw_remove,
|
||||
.ops = &rt711_slave_ops,
|
||||
.id_table = rt711_id,
|
||||
};
|
||||
module_sdw_driver(rt711_sdw_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT711 SDW driver");
|
||||
MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
|
||||
MODULE_LICENSE("GPL");
|
281
sound/soc/codecs/rt711-sdw.h
Normal file
281
sound/soc/codecs/rt711-sdw.h
Normal file
@ -0,0 +1,281 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt711-sdw.h -- RT711 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT711_SDW_H__
|
||||
#define __RT711_SDW_H__
|
||||
|
||||
static const struct reg_default rt711_reg_defaults[] = {
|
||||
{ 0x0000, 0x00 },
|
||||
{ 0x0001, 0x00 },
|
||||
{ 0x0002, 0x00 },
|
||||
{ 0x0003, 0x00 },
|
||||
{ 0x0004, 0x00 },
|
||||
{ 0x0005, 0x01 },
|
||||
{ 0x0020, 0x00 },
|
||||
{ 0x0022, 0x00 },
|
||||
{ 0x0023, 0x00 },
|
||||
{ 0x0024, 0x00 },
|
||||
{ 0x0025, 0x00 },
|
||||
{ 0x0026, 0x00 },
|
||||
{ 0x0030, 0x00 },
|
||||
{ 0x0032, 0x00 },
|
||||
{ 0x0033, 0x00 },
|
||||
{ 0x0034, 0x00 },
|
||||
{ 0x0035, 0x00 },
|
||||
{ 0x0036, 0x00 },
|
||||
{ 0x0040, 0x00 },
|
||||
{ 0x0041, 0x00 },
|
||||
{ 0x0042, 0x00 },
|
||||
{ 0x0043, 0x00 },
|
||||
{ 0x0044, 0x20 },
|
||||
{ 0x0045, 0x01 },
|
||||
{ 0x0046, 0x01 },
|
||||
{ 0x0050, 0x20 },
|
||||
{ 0x0051, 0x02 },
|
||||
{ 0x0052, 0x5d },
|
||||
{ 0x0053, 0x07 },
|
||||
{ 0x0054, 0x11 },
|
||||
{ 0x0055, 0x00 },
|
||||
{ 0x0060, 0x00 },
|
||||
{ 0x0070, 0x00 },
|
||||
{ 0x0080, 0xc0 },
|
||||
{ 0x0088, 0x00 },
|
||||
{ 0x00e0, 0x00 },
|
||||
{ 0x00e1, 0x00 },
|
||||
{ 0x00e2, 0x00 },
|
||||
{ 0x00e3, 0x00 },
|
||||
{ 0x00e5, 0x00 },
|
||||
{ 0x00ee, 0x00 },
|
||||
{ 0x00ef, 0x00 },
|
||||
{ 0x00f0, 0x00 },
|
||||
{ 0x00f1, 0x00 },
|
||||
{ 0x00f2, 0x00 },
|
||||
{ 0x00f3, 0x00 },
|
||||
{ 0x00f4, 0x00 },
|
||||
{ 0x00f5, 0x00 },
|
||||
{ 0x00fe, 0x00 },
|
||||
{ 0x00ff, 0x00 },
|
||||
{ 0x0100, 0x00 },
|
||||
{ 0x0101, 0x00 },
|
||||
{ 0x0102, 0x00 },
|
||||
{ 0x0103, 0x00 },
|
||||
{ 0x0104, 0x00 },
|
||||
{ 0x0105, 0x00 },
|
||||
{ 0x0120, 0x00 },
|
||||
{ 0x0122, 0x00 },
|
||||
{ 0x0123, 0x00 },
|
||||
{ 0x0124, 0x00 },
|
||||
{ 0x0125, 0x00 },
|
||||
{ 0x0126, 0x00 },
|
||||
{ 0x0127, 0x00 },
|
||||
{ 0x0130, 0x00 },
|
||||
{ 0x0132, 0x00 },
|
||||
{ 0x0133, 0x00 },
|
||||
{ 0x0134, 0x00 },
|
||||
{ 0x0135, 0x00 },
|
||||
{ 0x0136, 0x00 },
|
||||
{ 0x0137, 0x00 },
|
||||
{ 0x0200, 0x00 },
|
||||
{ 0x0201, 0x00 },
|
||||
{ 0x0202, 0x00 },
|
||||
{ 0x0203, 0x00 },
|
||||
{ 0x0204, 0x00 },
|
||||
{ 0x0205, 0x03 },
|
||||
{ 0x0220, 0x00 },
|
||||
{ 0x0222, 0x00 },
|
||||
{ 0x0223, 0x00 },
|
||||
{ 0x0224, 0x00 },
|
||||
{ 0x0225, 0x00 },
|
||||
{ 0x0226, 0x00 },
|
||||
{ 0x0227, 0x00 },
|
||||
{ 0x0230, 0x00 },
|
||||
{ 0x0232, 0x00 },
|
||||
{ 0x0233, 0x00 },
|
||||
{ 0x0234, 0x00 },
|
||||
{ 0x0235, 0x00 },
|
||||
{ 0x0236, 0x00 },
|
||||
{ 0x0237, 0x00 },
|
||||
{ 0x0300, 0x00 },
|
||||
{ 0x0301, 0x00 },
|
||||
{ 0x0302, 0x20 },
|
||||
{ 0x0303, 0x00 },
|
||||
{ 0x0304, 0x00 },
|
||||
{ 0x0305, 0x03 },
|
||||
{ 0x0320, 0x00 },
|
||||
{ 0x0322, 0x00 },
|
||||
{ 0x0323, 0x00 },
|
||||
{ 0x0324, 0x00 },
|
||||
{ 0x0325, 0x00 },
|
||||
{ 0x0326, 0x00 },
|
||||
{ 0x0327, 0x00 },
|
||||
{ 0x0330, 0x00 },
|
||||
{ 0x0332, 0x00 },
|
||||
{ 0x0333, 0x00 },
|
||||
{ 0x0334, 0x00 },
|
||||
{ 0x0335, 0x00 },
|
||||
{ 0x0336, 0x00 },
|
||||
{ 0x0337, 0x00 },
|
||||
{ 0x0400, 0x00 },
|
||||
{ 0x0401, 0x00 },
|
||||
{ 0x0402, 0x00 },
|
||||
{ 0x0403, 0x00 },
|
||||
{ 0x0404, 0x00 },
|
||||
{ 0x0405, 0x03 },
|
||||
{ 0x0420, 0x00 },
|
||||
{ 0x0422, 0x00 },
|
||||
{ 0x0423, 0x00 },
|
||||
{ 0x0424, 0x00 },
|
||||
{ 0x0425, 0x00 },
|
||||
{ 0x0426, 0x00 },
|
||||
{ 0x0427, 0x00 },
|
||||
{ 0x0430, 0x00 },
|
||||
{ 0x0432, 0x00 },
|
||||
{ 0x0433, 0x00 },
|
||||
{ 0x0434, 0x00 },
|
||||
{ 0x0435, 0x00 },
|
||||
{ 0x0436, 0x00 },
|
||||
{ 0x0437, 0x00 },
|
||||
{ 0x0f00, 0x00 },
|
||||
{ 0x0f01, 0x00 },
|
||||
{ 0x0f02, 0x20 },
|
||||
{ 0x0f03, 0x00 },
|
||||
{ 0x0f04, 0x00 },
|
||||
{ 0x0f05, 0x03 },
|
||||
{ 0x0f06, 0x00 },
|
||||
{ 0x0f07, 0x00 },
|
||||
{ 0x0f08, 0x00 },
|
||||
{ 0x0f09, 0x00 },
|
||||
{ 0x0f10, 0x00 },
|
||||
{ 0x0f11, 0x00 },
|
||||
{ 0x0f12, 0x00 },
|
||||
{ 0x0f13, 0x00 },
|
||||
{ 0x0f14, 0x00 },
|
||||
{ 0x0f15, 0x00 },
|
||||
{ 0x0f16, 0x00 },
|
||||
{ 0x0f17, 0x00 },
|
||||
{ 0x0f18, 0x00 },
|
||||
{ 0x0f19, 0x00 },
|
||||
{ 0x0f1a, 0x00 },
|
||||
{ 0x0f1b, 0x00 },
|
||||
{ 0x0f1c, 0x00 },
|
||||
{ 0x0f1d, 0x00 },
|
||||
{ 0x0f1e, 0x00 },
|
||||
{ 0x0f1f, 0x00 },
|
||||
{ 0x0f20, 0x00 },
|
||||
{ 0x0f22, 0x00 },
|
||||
{ 0x0f23, 0x00 },
|
||||
{ 0x0f24, 0x00 },
|
||||
{ 0x0f25, 0x00 },
|
||||
{ 0x0f26, 0x00 },
|
||||
{ 0x0f27, 0x00 },
|
||||
{ 0x0f30, 0x00 },
|
||||
{ 0x0f32, 0x00 },
|
||||
{ 0x0f33, 0x00 },
|
||||
{ 0x0f34, 0x00 },
|
||||
{ 0x0f35, 0x00 },
|
||||
{ 0x0f36, 0x00 },
|
||||
{ 0x0f37, 0x00 },
|
||||
{ 0x2012, 0x00 },
|
||||
{ 0x2013, 0x00 },
|
||||
{ 0x2014, 0x00 },
|
||||
{ 0x2015, 0x00 },
|
||||
{ 0x2016, 0x00 },
|
||||
{ 0x201a, 0x00 },
|
||||
{ 0x201b, 0x00 },
|
||||
{ 0x201c, 0x0c },
|
||||
{ 0x201d, 0x00 },
|
||||
{ 0x201e, 0x00 },
|
||||
{ 0x201f, 0x00 },
|
||||
{ 0x2020, 0x00 },
|
||||
{ 0x2021, 0x00 },
|
||||
{ 0x2022, 0x00 },
|
||||
{ 0x2023, 0x00 },
|
||||
{ 0x2024, 0x00 },
|
||||
{ 0x2025, 0x01 },
|
||||
{ 0x2026, 0x00 },
|
||||
{ 0x2027, 0x00 },
|
||||
{ 0x2029, 0x00 },
|
||||
{ 0x202a, 0x00 },
|
||||
{ 0x202d, 0x00 },
|
||||
{ 0x202e, 0x00 },
|
||||
{ 0x202f, 0x00 },
|
||||
{ 0x2030, 0x00 },
|
||||
{ 0x2031, 0x00 },
|
||||
{ 0x2032, 0x00 },
|
||||
{ 0x2033, 0x00 },
|
||||
{ 0x2034, 0x00 },
|
||||
{ 0x2201, 0xc7 },
|
||||
{ 0x2202, 0x0c },
|
||||
{ 0x2203, 0x22 },
|
||||
{ 0x2204, 0x04 },
|
||||
{ 0x2206, 0x00 },
|
||||
{ 0x2207, 0x00 },
|
||||
{ 0x2208, 0x00 },
|
||||
{ 0x2209, 0x00 },
|
||||
{ 0x220a, 0x00 },
|
||||
{ 0x220b, 0x00 },
|
||||
{ 0x220c, 0x00 },
|
||||
{ 0x220d, 0x04 },
|
||||
{ 0x220e, 0x00 },
|
||||
{ 0x220f, 0x00 },
|
||||
{ 0x2211, 0x01 },
|
||||
{ 0x2212, 0x00 },
|
||||
{ 0x2220, 0x00 },
|
||||
{ 0x2221, 0x00 },
|
||||
{ 0x2222, 0x00 },
|
||||
{ 0x2223, 0x00 },
|
||||
{ 0x2230, 0x00 },
|
||||
{ 0x2231, 0x2f },
|
||||
{ 0x2232, 0x80 },
|
||||
{ 0x2233, 0x00 },
|
||||
{ 0x2234, 0x00 },
|
||||
{ 0x2235, 0x00 },
|
||||
{ 0x2236, 0x00 },
|
||||
{ 0x2237, 0x00 },
|
||||
{ 0x2238, 0x00 },
|
||||
{ 0x2239, 0x00 },
|
||||
{ 0x2f01, 0x00 },
|
||||
{ 0x2f02, 0x09 },
|
||||
{ 0x2f03, 0x00 },
|
||||
{ 0x2f04, 0x00 },
|
||||
{ 0x2f05, 0x0b },
|
||||
{ 0x2f06, 0x01 },
|
||||
{ 0x2f07, 0xcf },
|
||||
{ 0x2f08, 0x00 },
|
||||
{ 0x2f09, 0x00 },
|
||||
{ 0x2f0a, 0x00 },
|
||||
{ 0x2f0b, 0x00 },
|
||||
{ 0x2f0c, 0x00 },
|
||||
{ 0x2f0d, 0x00 },
|
||||
{ 0x2f0e, 0x00 },
|
||||
{ 0x2f0f, 0x00 },
|
||||
{ 0x3122, 0x00 },
|
||||
{ 0x3123, 0x00 },
|
||||
{ 0x7303, 0x57 },
|
||||
{ 0x8383, 0x57 },
|
||||
{ 0x7308, 0x97 },
|
||||
{ 0x8388, 0x97 },
|
||||
{ 0x7309, 0x97 },
|
||||
{ 0x8389, 0x97 },
|
||||
{ 0x7312, 0x00 },
|
||||
{ 0x8392, 0x00 },
|
||||
{ 0x7313, 0x00 },
|
||||
{ 0x8393, 0x00 },
|
||||
{ 0x7319, 0x00 },
|
||||
{ 0x8399, 0x00 },
|
||||
{ 0x752009, 0x1029 },
|
||||
{ 0x752011, 0x007a },
|
||||
{ 0x75201a, 0x8003 },
|
||||
{ 0x752045, 0x5289 },
|
||||
{ 0x752048, 0xd049 },
|
||||
{ 0x75204a, 0xa83b },
|
||||
{ 0x75206b, 0x5064 },
|
||||
{ 0x75206f, 0x058b },
|
||||
{ 0x752091, 0x0000 },
|
||||
};
|
||||
|
||||
#endif /* __RT711_SDW_H__ */
|
1292
sound/soc/codecs/rt711.c
Normal file
1292
sound/soc/codecs/rt711.c
Normal file
File diff suppressed because it is too large
Load Diff
227
sound/soc/codecs/rt711.h
Normal file
227
sound/soc/codecs/rt711.h
Normal file
@ -0,0 +1,227 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt711.h -- RT711 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT711_H__
|
||||
#define __RT711_H__
|
||||
|
||||
extern const struct dev_pm_ops rt711_runtime_pm;
|
||||
|
||||
struct rt711_priv {
|
||||
struct regmap *regmap;
|
||||
struct regmap *sdw_regmap;
|
||||
struct snd_soc_component *component;
|
||||
struct sdw_slave *slave;
|
||||
enum sdw_slave_status status;
|
||||
struct sdw_bus_params params;
|
||||
bool hw_init;
|
||||
bool first_hw_init;
|
||||
struct snd_soc_jack *hs_jack;
|
||||
struct delayed_work jack_detect_work;
|
||||
struct delayed_work jack_btn_check_work;
|
||||
struct work_struct calibration_work;
|
||||
struct mutex calibrate_mutex; /* for headset calibration */
|
||||
int jack_type, jd_src;
|
||||
};
|
||||
|
||||
struct sdw_stream_data {
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
};
|
||||
|
||||
/* NID */
|
||||
#define RT711_AUDIO_FUNCTION_GROUP 0x01
|
||||
#define RT711_DAC_OUT2 0x03
|
||||
#define RT711_ADC_IN1 0x09
|
||||
#define RT711_ADC_IN2 0x08
|
||||
#define RT711_DMIC1 0x12
|
||||
#define RT711_DMIC2 0x13
|
||||
#define RT711_MIC2 0x19
|
||||
#define RT711_LINE1 0x1a
|
||||
#define RT711_LINE2 0x1b
|
||||
#define RT711_BEEP 0x1d
|
||||
#define RT711_VENDOR_REG 0x20
|
||||
#define RT711_HP_OUT 0x21
|
||||
#define RT711_MIXER_IN1 0x22
|
||||
#define RT711_MIXER_IN2 0x23
|
||||
#define RT711_INLINE_CMD 0x55
|
||||
#define RT711_VENDOR_CALI 0x58
|
||||
#define RT711_VENDOR_IMS_DRE 0x5b
|
||||
|
||||
/* Index (NID:20h) */
|
||||
#define RT711_DAC_DC_CALI_CTL1 0x00
|
||||
#define RT711_JD_CTL2 0x09
|
||||
#define RT711_CC_DET1 0x11
|
||||
#define RT711_PARA_VERB_CTL 0x1a
|
||||
#define RT711_COMBO_JACK_AUTO_CTL1 0x45
|
||||
#define RT711_COMBO_JACK_AUTO_CTL2 0x46
|
||||
#define RT711_INLINE_CMD_CTL 0x48
|
||||
#define RT711_DIGITAL_MISC_CTRL4 0x4a
|
||||
#define RT711_VREFOUT_CTL 0x6b
|
||||
#define RT711_FSM_CTL 0x6f
|
||||
#define RT711_IRQ_FLAG_TABLE1 0x80
|
||||
#define RT711_IRQ_FLAG_TABLE2 0x81
|
||||
#define RT711_IRQ_FLAG_TABLE3 0x82
|
||||
#define RT711_TX_RX_MUX_CTL 0x91
|
||||
|
||||
/* Index (NID:5bh) */
|
||||
#define RT711_IMS_DIGITAL_CTL1 0x00
|
||||
#define RT711_HP_IMS_RESULT_L 0x20
|
||||
#define RT711_HP_IMS_RESULT_R 0x21
|
||||
|
||||
/* Verb */
|
||||
#define RT711_VERB_SET_CONNECT_SEL 0x3100
|
||||
#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00
|
||||
#define RT711_VERB_GET_CONNECT_SEL 0xb100
|
||||
#define RT711_VERB_SET_POWER_STATE 0x3500
|
||||
#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600
|
||||
#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700
|
||||
#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800
|
||||
#define RT711_SET_AMP_GAIN_MUTE_H 0x7300
|
||||
#define RT711_SET_AMP_GAIN_MUTE_L 0x8380
|
||||
#define RT711_VERB_GET_POWER_STATE 0xb500
|
||||
#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600
|
||||
#define RT711_VERB_GET_PIN_SENSE 0xb900
|
||||
#define RT711_FUNC_RESET 0xff01
|
||||
|
||||
#define RT711_READ_HDA_3 0x2012
|
||||
#define RT711_READ_HDA_2 0x2013
|
||||
#define RT711_READ_HDA_1 0x2014
|
||||
#define RT711_READ_HDA_0 0x2015
|
||||
#define RT711_PRIV_INDEX_W_H 0x7500
|
||||
#define RT711_PRIV_INDEX_W_L 0x8580
|
||||
#define RT711_PRIV_DATA_W_H 0x7400
|
||||
#define RT711_PRIV_DATA_W_L 0x8480
|
||||
#define RT711_PRIV_INDEX_R_H 0x9d00
|
||||
#define RT711_PRIV_INDEX_R_L 0xad80
|
||||
#define RT711_PRIV_DATA_R_H 0x9c00
|
||||
#define RT711_PRIV_DATA_R_L 0xac80
|
||||
#define RT711_DAC_FORMAT_H 0x7203
|
||||
#define RT711_DAC_FORMAT_L 0x8283
|
||||
#define RT711_ADC1_FORMAT_H 0x7209
|
||||
#define RT711_ADC1_FORMAT_L 0x8289
|
||||
#define RT711_ADC2_FORMAT_H 0x7208
|
||||
#define RT711_ADC2_FORMAT_L 0x8288
|
||||
|
||||
#define RT711_SET_AUDIO_POWER_STATE\
|
||||
(RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
|
||||
#define RT711_GET_AUDIO_POWER_STATE\
|
||||
(RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
|
||||
#define RT711_SET_PIN_DMIC1\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1)
|
||||
#define RT711_SET_PIN_DMIC2\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2)
|
||||
#define RT711_SET_PIN_HP\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT)
|
||||
#define RT711_SET_PIN_MIC2\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2)
|
||||
#define RT711_SET_PIN_LINE1\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1)
|
||||
#define RT711_SET_PIN_LINE2\
|
||||
(RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2)
|
||||
#define RT711_SET_MIC2_UNSOLICITED_ENABLE\
|
||||
(RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2)
|
||||
#define RT711_SET_HP_UNSOLICITED_ENABLE\
|
||||
(RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT)
|
||||
#define RT711_SET_INLINE_UNSOLICITED_ENABLE\
|
||||
(RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD)
|
||||
#define RT711_SET_STREAMID_DAC2\
|
||||
(RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2)
|
||||
#define RT711_SET_STREAMID_ADC1\
|
||||
(RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1)
|
||||
#define RT711_SET_STREAMID_ADC2\
|
||||
(RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2)
|
||||
#define RT711_GET_STREAMID_DAC2\
|
||||
(RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2)
|
||||
#define RT711_GET_STREAMID_ADC1\
|
||||
(RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1)
|
||||
#define RT711_GET_STREAMID_ADC2\
|
||||
(RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2)
|
||||
#define RT711_SET_GAIN_DAC2_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2)
|
||||
#define RT711_SET_GAIN_DAC2_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2)
|
||||
#define RT711_SET_GAIN_ADC1_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1)
|
||||
#define RT711_SET_GAIN_ADC1_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1)
|
||||
#define RT711_SET_GAIN_ADC2_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2)
|
||||
#define RT711_SET_GAIN_ADC2_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2)
|
||||
#define RT711_SET_GAIN_AMIC_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2)
|
||||
#define RT711_SET_GAIN_AMIC_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2)
|
||||
#define RT711_SET_GAIN_DMIC1_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1)
|
||||
#define RT711_SET_GAIN_DMIC1_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1)
|
||||
#define RT711_SET_GAIN_DMIC2_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2)
|
||||
#define RT711_SET_GAIN_DMIC2_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2)
|
||||
#define RT711_SET_GAIN_HP_L\
|
||||
(RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT)
|
||||
#define RT711_SET_GAIN_HP_H\
|
||||
(RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT)
|
||||
|
||||
/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
|
||||
#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
|
||||
|
||||
/* jack detect control 2 (0x09)(NID:20h) */
|
||||
#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
|
||||
#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
|
||||
#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
|
||||
|
||||
/* CC DET1 (0x11)(NID:20h) */
|
||||
#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
|
||||
#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
|
||||
|
||||
/* Parameter & Verb control (0x1a)(NID:20h) */
|
||||
#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
|
||||
|
||||
/* combo jack auto switch control 2 (0x46)(NID:20h) */
|
||||
#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
|
||||
#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
|
||||
#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
|
||||
#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
|
||||
|
||||
/* FSM control (0x6f)(NID:20h) */
|
||||
#define RT711_CALI_CTL (0x0 << 0)
|
||||
#define RT711_COMBOJACK_CTL (0x1 << 0)
|
||||
#define RT711_IMS_CTL (0x2 << 0)
|
||||
#define RT711_DEPOP_CTL (0x3 << 0)
|
||||
|
||||
/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
|
||||
#define RT711_TRIGGER_IMS (0x1 << 15)
|
||||
#define RT711_IMS_EN (0x1 << 6)
|
||||
|
||||
#define RT711_EAPD_HIGH 0x2
|
||||
#define RT711_EAPD_LOW 0x0
|
||||
#define RT711_MUTE_SFT 7
|
||||
/* set input/output mapping to payload[14][15] separately */
|
||||
#define RT711_DIR_IN_SFT 6
|
||||
#define RT711_DIR_OUT_SFT 7
|
||||
|
||||
enum {
|
||||
RT711_AIF1,
|
||||
RT711_AIF2,
|
||||
RT711_AIFS,
|
||||
};
|
||||
|
||||
enum rt711_jd_src {
|
||||
RT711_JD_NULL,
|
||||
RT711_JD1,
|
||||
RT711_JD2
|
||||
};
|
||||
|
||||
int rt711_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt711_init(struct device *dev, struct regmap *sdw_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave);
|
||||
|
||||
int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic);
|
||||
int rt711_clock_config(struct device *dev);
|
||||
#endif /* __RT711_H__ */
|
613
sound/soc/codecs/rt715-sdw.c
Normal file
613
sound/soc/codecs/rt715-sdw.c
Normal file
@ -0,0 +1,613 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* rt715-sdw.c -- rt715 ALSA SoC audio driver
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*
|
||||
* ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
|
||||
*
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include "rt715.h"
|
||||
#include "rt715-sdw.h"
|
||||
|
||||
static bool rt715_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00e0 ... 0x00e5:
|
||||
case 0x00ee ... 0x00ef:
|
||||
case 0x00f0 ... 0x00f5:
|
||||
case 0x00fe ... 0x00ff:
|
||||
case 0x02e0:
|
||||
case 0x02f0:
|
||||
case 0x04e0:
|
||||
case 0x04f0:
|
||||
case 0x06e0:
|
||||
case 0x06f0:
|
||||
case 0x2000 ... 0x2016:
|
||||
case 0x201a ... 0x2027:
|
||||
case 0x2029 ... 0x202a:
|
||||
case 0x202d ... 0x2034:
|
||||
case 0x2200 ... 0x2204:
|
||||
case 0x2206 ... 0x2212:
|
||||
case 0x2220 ... 0x2223:
|
||||
case 0x2230 ... 0x2239:
|
||||
case 0x22f0 ... 0x22f3:
|
||||
case 0x3122:
|
||||
case 0x3123:
|
||||
case 0x3124:
|
||||
case 0x3125:
|
||||
case 0x3607:
|
||||
case 0x3608:
|
||||
case 0x3609:
|
||||
case 0x3610:
|
||||
case 0x3611:
|
||||
case 0x3627:
|
||||
case 0x3712:
|
||||
case 0x3713:
|
||||
case 0x3718:
|
||||
case 0x3719:
|
||||
case 0x371a:
|
||||
case 0x371b:
|
||||
case 0x371d:
|
||||
case 0x3729:
|
||||
case 0x385e:
|
||||
case 0x3859:
|
||||
case 0x4c12:
|
||||
case 0x4c13:
|
||||
case 0x4c1d:
|
||||
case 0x4c29:
|
||||
case 0x4d12:
|
||||
case 0x4d13:
|
||||
case 0x4d1d:
|
||||
case 0x4d29:
|
||||
case 0x4e12:
|
||||
case 0x4e13:
|
||||
case 0x4e1d:
|
||||
case 0x4e29:
|
||||
case 0x4f12:
|
||||
case 0x4f13:
|
||||
case 0x4f1d:
|
||||
case 0x4f29:
|
||||
case 0x7207:
|
||||
case 0x7208:
|
||||
case 0x7209:
|
||||
case 0x7227:
|
||||
case 0x7307:
|
||||
case 0x7308:
|
||||
case 0x7309:
|
||||
case 0x7312:
|
||||
case 0x7313:
|
||||
case 0x7318:
|
||||
case 0x7319:
|
||||
case 0x731a:
|
||||
case 0x731b:
|
||||
case 0x731d:
|
||||
case 0x7327:
|
||||
case 0x7329:
|
||||
case 0x8287:
|
||||
case 0x8288:
|
||||
case 0x8289:
|
||||
case 0x82a7:
|
||||
case 0x8387:
|
||||
case 0x8388:
|
||||
case 0x8389:
|
||||
case 0x8392:
|
||||
case 0x8393:
|
||||
case 0x8398:
|
||||
case 0x8399:
|
||||
case 0x839a:
|
||||
case 0x839b:
|
||||
case 0x839d:
|
||||
case 0x83a7:
|
||||
case 0x83a9:
|
||||
case 0x752039:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt715_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00e5:
|
||||
case 0x00f0:
|
||||
case 0x00f3:
|
||||
case 0x00f5:
|
||||
case 0x2009:
|
||||
case 0x2016:
|
||||
case 0x201b:
|
||||
case 0x201c:
|
||||
case 0x201d:
|
||||
case 0x201f:
|
||||
case 0x2023:
|
||||
case 0x2230:
|
||||
case 0x200b ... 0x200e: /* i2c read */
|
||||
case 0x2012 ... 0x2015: /* HD-A read */
|
||||
case 0x202d ... 0x202f: /* BRA */
|
||||
case 0x2201 ... 0x2212: /* i2c debug */
|
||||
case 0x2220 ... 0x2223: /* decoded HD-A */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
|
||||
unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
|
||||
unsigned int is_hda_reg = 1, is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT715_PRIV_DATA_R_H | nid;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg3,
|
||||
((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x3000) {
|
||||
reg += 0x8000;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
reg += 0x2000;
|
||||
reg |= 0x800;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg,
|
||||
((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2,
|
||||
((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x9000) {
|
||||
ret = regmap_write(rt715->sdw_regmap, reg,
|
||||
((*val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0xb000) {
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, *val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regmap_read(rt715->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_hda_reg = 0;
|
||||
}
|
||||
|
||||
if (is_hda_reg || is_index_reg) {
|
||||
sdw_data_3 = 0;
|
||||
sdw_data_2 = 0;
|
||||
sdw_data_1 = 0;
|
||||
sdw_data_0 = 0;
|
||||
ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3,
|
||||
&sdw_data_3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2,
|
||||
&sdw_data_2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1,
|
||||
&sdw_data_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0,
|
||||
&sdw_data_0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ((sdw_data_3 & 0xff) << 24) |
|
||||
((sdw_data_2 & 0xff) << 16) |
|
||||
((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
|
||||
}
|
||||
|
||||
if (is_hda_reg == 0)
|
||||
dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__,
|
||||
reg, reg2, reg3, reg4, *val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x => %08x\n",
|
||||
__func__, reg, reg2, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
|
||||
unsigned int is_index_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (reg > 0xffff)
|
||||
is_index_reg = 1;
|
||||
|
||||
mask = reg & 0xf000;
|
||||
|
||||
if (is_index_reg) { /* index registers */
|
||||
val2 = reg & 0xff;
|
||||
reg = reg >> 8;
|
||||
nid = reg & 0xff;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg3 = RT715_PRIV_DATA_W_H | nid;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg3,
|
||||
((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg4 = reg3 + 0x1000;
|
||||
reg4 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
is_index_reg = 1;
|
||||
} else if (reg < 0x4fff) {
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (reg == RT715_FUNC_RESET) {
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (mask == 0x7000) {
|
||||
ret = regmap_write(rt715->sdw_regmap, reg,
|
||||
((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reg2 = reg + 0x1000;
|
||||
reg2 |= 0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if ((reg & 0xff00) == 0x8300) { /* for R channel */
|
||||
reg2 = reg - 0x1000;
|
||||
reg2 &= ~0x80;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg2,
|
||||
((val >> 8) & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg2 == 0)
|
||||
dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
|
||||
else if (is_index_reg)
|
||||
dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
|
||||
__func__, reg, reg2, reg3, reg4, val2, val);
|
||||
else
|
||||
dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
|
||||
__func__, reg, reg2, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config rt715_regmap = {
|
||||
.reg_bits = 24,
|
||||
.val_bits = 32,
|
||||
.readable_reg = rt715_readable_register, /* Readable registers */
|
||||
.volatile_reg = rt715_volatile_register, /* volatile register */
|
||||
.max_register = 0x752039, /* Maximum number of register */
|
||||
.reg_defaults = rt715_reg_defaults, /* Defaults */
|
||||
.num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
.reg_read = rt715_sdw_read,
|
||||
.reg_write = rt715_sdw_write,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt715_sdw_regmap = {
|
||||
.name = "sdw",
|
||||
.reg_bits = 32, /* Total register space for SDW */
|
||||
.val_bits = 8, /* Total number of bits in register */
|
||||
.max_register = 0xff01, /* Maximum number of register */
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
|
||||
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
|
||||
unsigned int *sdw_addr_l, unsigned int *sdw_data_l)
|
||||
{
|
||||
unsigned int offset_h, offset_l, e_verb;
|
||||
|
||||
if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */
|
||||
if (verb == 0x7ff) /* special case */
|
||||
offset_h = 0;
|
||||
else
|
||||
offset_h = 0x3000;
|
||||
|
||||
if (verb & 0x800) /* get command */
|
||||
e_verb = (verb - 0xf00) | 0x80;
|
||||
else /* set command */
|
||||
e_verb = (verb - 0x700);
|
||||
|
||||
*sdw_data_h = payload; /* 7 bits payload */
|
||||
*sdw_addr_l = *sdw_data_l = 0;
|
||||
} else { /* 4 bits command */
|
||||
if ((verb & 0x800) == 0x800) { /* read */
|
||||
offset_h = 0x9000;
|
||||
offset_l = 0xa000;
|
||||
} else { /* write */
|
||||
offset_h = 0x7000;
|
||||
offset_l = 0x8000;
|
||||
}
|
||||
e_verb = verb >> 8;
|
||||
*sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */
|
||||
*sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */
|
||||
*sdw_addr_l += offset_l;
|
||||
*sdw_data_l = payload & 0xff;
|
||||
}
|
||||
|
||||
*sdw_addr_h = (e_verb << 8) | nid;
|
||||
*sdw_addr_h += offset_h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hda_to_sdw);
|
||||
|
||||
static int rt715_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
/* Update the status */
|
||||
rt715->status = status;
|
||||
/*
|
||||
* Perform initialization only if slave status is present and
|
||||
* hw_init flag is false
|
||||
*/
|
||||
if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED)
|
||||
return 0;
|
||||
|
||||
/* perform I/O transfers required for Slave initialization */
|
||||
return rt715_io_init(&slave->dev, slave);
|
||||
}
|
||||
|
||||
static int rt715_read_prop(struct sdw_slave *slave)
|
||||
{
|
||||
struct sdw_slave_prop *prop = &slave->prop;
|
||||
int nval, i, num_of_ports = 1;
|
||||
u32 bit;
|
||||
unsigned long addr;
|
||||
struct sdw_dpn_prop *dpn;
|
||||
|
||||
prop->paging_support = false;
|
||||
|
||||
/* first we need to allocate memory for set bits in port lists */
|
||||
prop->source_ports = 0x50;/* BITMAP: 01010000 */
|
||||
prop->sink_ports = 0x0; /* BITMAP: 00000000 */
|
||||
|
||||
nval = hweight32(prop->source_ports);
|
||||
num_of_ports += nval;
|
||||
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->src_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->src_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
dpn = prop->src_dpn_prop;
|
||||
i = 0;
|
||||
addr = prop->source_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* do this again for sink now */
|
||||
nval = hweight32(prop->sink_ports);
|
||||
num_of_ports += nval;
|
||||
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->sink_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->sink_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
dpn = prop->sink_dpn_prop;
|
||||
i = 0;
|
||||
addr = prop->sink_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Allocate port_ready based on num_of_ports */
|
||||
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
|
||||
sizeof(*slave->port_ready),
|
||||
GFP_KERNEL);
|
||||
if (!slave->port_ready)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize completion */
|
||||
for (i = 0; i < num_of_ports; i++)
|
||||
init_completion(&slave->port_ready[i]);
|
||||
|
||||
/* set the timeout values */
|
||||
prop->clk_stop_timeout = 20;
|
||||
|
||||
/* wake-up event */
|
||||
prop->wake_capable = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_bus_config(struct sdw_slave *slave,
|
||||
struct sdw_bus_params *params)
|
||||
{
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
|
||||
int ret;
|
||||
|
||||
memcpy(&rt715->params, params, sizeof(*params));
|
||||
|
||||
ret = rt715_clock_config(&slave->dev);
|
||||
if (ret < 0)
|
||||
dev_err(&slave->dev, "Invalid clk config");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdw_slave_ops rt715_slave_ops = {
|
||||
.read_prop = rt715_read_prop,
|
||||
.update_status = rt715_update_status,
|
||||
.bus_config = rt715_bus_config,
|
||||
};
|
||||
|
||||
static int rt715_sdw_probe(struct sdw_slave *slave,
|
||||
const struct sdw_device_id *id)
|
||||
{
|
||||
struct regmap *sdw_regmap, *regmap;
|
||||
|
||||
/* Assign ops */
|
||||
slave->ops = &rt715_slave_ops;
|
||||
|
||||
/* Regmap Initialization */
|
||||
sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap);
|
||||
if (!sdw_regmap)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev,
|
||||
&rt715_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
rt715_init(&slave->dev, sdw_regmap, regmap, slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdw_device_id rt715_id[] = {
|
||||
SDW_SLAVE_ENTRY(0x025d, 0x715, 0),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdw, rt715_id);
|
||||
|
||||
static int rt715_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
|
||||
if (!rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(rt715->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT715_PROBE_TIMEOUT 2000
|
||||
|
||||
static int rt715_dev_resume(struct device *dev)
|
||||
{
|
||||
struct sdw_slave *slave = dev_to_sdw_dev(dev);
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
if (!rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
if (!slave->unattach_request)
|
||||
goto regmap_sync;
|
||||
|
||||
time = wait_for_completion_timeout(&slave->initialization_complete,
|
||||
msecs_to_jiffies(RT715_PROBE_TIMEOUT));
|
||||
if (!time) {
|
||||
dev_err(&slave->dev, "Initialization not complete, timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_sync:
|
||||
slave->unattach_request = 0;
|
||||
regcache_cache_only(rt715->regmap, false);
|
||||
regcache_sync_region(rt715->regmap, 0x3000, 0x8fff);
|
||||
regcache_sync_region(rt715->regmap, 0x752039, 0x752039);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rt715_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
|
||||
SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
|
||||
};
|
||||
|
||||
static struct sdw_driver rt715_sdw_driver = {
|
||||
.driver = {
|
||||
.name = "rt715",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rt715_pm,
|
||||
},
|
||||
.probe = rt715_sdw_probe,
|
||||
.ops = &rt715_slave_ops,
|
||||
.id_table = rt715_id,
|
||||
};
|
||||
module_sdw_driver(rt715_sdw_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT715 driver SDW");
|
||||
MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
337
sound/soc/codecs/rt715-sdw.h
Normal file
337
sound/soc/codecs/rt715-sdw.h
Normal file
@ -0,0 +1,337 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt715-sdw.h -- RT715 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT715_SDW_H__
|
||||
#define __RT715_SDW_H__
|
||||
|
||||
static const struct reg_default rt715_reg_defaults[] = {
|
||||
{ 0x0000, 0x00 },
|
||||
{ 0x0001, 0x00 },
|
||||
{ 0x0002, 0x00 },
|
||||
{ 0x0003, 0x00 },
|
||||
{ 0x0004, 0x00 },
|
||||
{ 0x0005, 0x01 },
|
||||
{ 0x0020, 0x00 },
|
||||
{ 0x0022, 0x00 },
|
||||
{ 0x0023, 0x00 },
|
||||
{ 0x0024, 0x00 },
|
||||
{ 0x0025, 0x00 },
|
||||
{ 0x0026, 0x00 },
|
||||
{ 0x0030, 0x00 },
|
||||
{ 0x0032, 0x00 },
|
||||
{ 0x0033, 0x00 },
|
||||
{ 0x0034, 0x00 },
|
||||
{ 0x0035, 0x00 },
|
||||
{ 0x0036, 0x00 },
|
||||
{ 0x0040, 0x00 },
|
||||
{ 0x0041, 0x00 },
|
||||
{ 0x0042, 0x00 },
|
||||
{ 0x0043, 0x00 },
|
||||
{ 0x0044, 0x20 },
|
||||
{ 0x0045, 0x01 },
|
||||
{ 0x0046, 0x00 },
|
||||
{ 0x0050, 0x20 },
|
||||
{ 0x0051, 0x02 },
|
||||
{ 0x0052, 0x5d },
|
||||
{ 0x0053, 0x07 },
|
||||
{ 0x0054, 0x15 },
|
||||
{ 0x0055, 0x00 },
|
||||
{ 0x0060, 0x00 },
|
||||
{ 0x0070, 0x00 },
|
||||
{ 0x0080, 0x00 },
|
||||
{ 0x0088, 0x10 },
|
||||
{ 0x00e0, 0x00 },
|
||||
{ 0x00e1, 0x00 },
|
||||
{ 0x00e2, 0x00 },
|
||||
{ 0x00e3, 0x00 },
|
||||
{ 0x00e4, 0x00 },
|
||||
{ 0x00e5, 0x00 },
|
||||
{ 0x00ee, 0x00 },
|
||||
{ 0x00ef, 0x00 },
|
||||
{ 0x00f0, 0x00 },
|
||||
{ 0x00f1, 0x00 },
|
||||
{ 0x00f2, 0x00 },
|
||||
{ 0x00f3, 0x00 },
|
||||
{ 0x00f4, 0x00 },
|
||||
{ 0x00f5, 0x00 },
|
||||
{ 0x00fe, 0x00 },
|
||||
{ 0x00ff, 0x00 },
|
||||
{ 0x0200, 0x00 },
|
||||
{ 0x0201, 0x00 },
|
||||
{ 0x0202, 0x20 },
|
||||
{ 0x0203, 0x00 },
|
||||
{ 0x0204, 0x00 },
|
||||
{ 0x0205, 0x03 },
|
||||
{ 0x0220, 0x00 },
|
||||
{ 0x0221, 0x00 },
|
||||
{ 0x0222, 0x00 },
|
||||
{ 0x0223, 0x00 },
|
||||
{ 0x0224, 0x00 },
|
||||
{ 0x0225, 0x00 },
|
||||
{ 0x0226, 0x00 },
|
||||
{ 0x0227, 0x00 },
|
||||
{ 0x0230, 0x00 },
|
||||
{ 0x0231, 0x00 },
|
||||
{ 0x0232, 0x00 },
|
||||
{ 0x0233, 0x00 },
|
||||
{ 0x0234, 0x00 },
|
||||
{ 0x0235, 0x00 },
|
||||
{ 0x0236, 0x00 },
|
||||
{ 0x0237, 0x00 },
|
||||
{ 0x02e0, 0x00 },
|
||||
{ 0x02f0, 0x00 },
|
||||
{ 0x0400, 0x00 },
|
||||
{ 0x0401, 0x00 },
|
||||
{ 0x0402, 0x20 },
|
||||
{ 0x0403, 0x00 },
|
||||
{ 0x0404, 0x00 },
|
||||
{ 0x0405, 0x0f },
|
||||
{ 0x0420, 0x00 },
|
||||
{ 0x0421, 0x00 },
|
||||
{ 0x0422, 0x00 },
|
||||
{ 0x0423, 0x00 },
|
||||
{ 0x0424, 0x00 },
|
||||
{ 0x0425, 0x00 },
|
||||
{ 0x0426, 0x00 },
|
||||
{ 0x0427, 0x00 },
|
||||
{ 0x0430, 0x00 },
|
||||
{ 0x0431, 0x00 },
|
||||
{ 0x0432, 0x00 },
|
||||
{ 0x0433, 0x00 },
|
||||
{ 0x0434, 0x00 },
|
||||
{ 0x0435, 0x00 },
|
||||
{ 0x0436, 0x00 },
|
||||
{ 0x0437, 0x00 },
|
||||
{ 0x04e0, 0x00 },
|
||||
{ 0x04f0, 0x00 },
|
||||
{ 0x0600, 0x00 },
|
||||
{ 0x0601, 0x00 },
|
||||
{ 0x0602, 0x20 },
|
||||
{ 0x0603, 0x00 },
|
||||
{ 0x0604, 0x00 },
|
||||
{ 0x0605, 0xff },
|
||||
{ 0x0620, 0x00 },
|
||||
{ 0x0621, 0x00 },
|
||||
{ 0x0622, 0x00 },
|
||||
{ 0x0623, 0x00 },
|
||||
{ 0x0624, 0x00 },
|
||||
{ 0x0625, 0x00 },
|
||||
{ 0x0626, 0x00 },
|
||||
{ 0x0627, 0x00 },
|
||||
{ 0x0630, 0x00 },
|
||||
{ 0x0631, 0x00 },
|
||||
{ 0x0632, 0x00 },
|
||||
{ 0x0633, 0x00 },
|
||||
{ 0x0634, 0x00 },
|
||||
{ 0x0635, 0x00 },
|
||||
{ 0x0636, 0x00 },
|
||||
{ 0x0637, 0x00 },
|
||||
{ 0x06e0, 0x00 },
|
||||
{ 0x06f0, 0x00 },
|
||||
{ 0x0f00, 0x00 },
|
||||
{ 0x0f01, 0x00 },
|
||||
{ 0x0f02, 0x00 },
|
||||
{ 0x0f03, 0x00 },
|
||||
{ 0x0f04, 0x00 },
|
||||
{ 0x0f05, 0xff },
|
||||
{ 0x0f06, 0x00 },
|
||||
{ 0x0f07, 0x00 },
|
||||
{ 0x0f08, 0x00 },
|
||||
{ 0x0f09, 0x00 },
|
||||
{ 0x0f0a, 0x00 },
|
||||
{ 0x0f0b, 0x00 },
|
||||
{ 0x0f0c, 0x00 },
|
||||
{ 0x0f0d, 0x00 },
|
||||
{ 0x0f0e, 0x00 },
|
||||
{ 0x0f0f, 0x00 },
|
||||
{ 0x0f10, 0x00 },
|
||||
{ 0x0f11, 0x00 },
|
||||
{ 0x0f12, 0x00 },
|
||||
{ 0x0f13, 0x00 },
|
||||
{ 0x0f14, 0x00 },
|
||||
{ 0x0f15, 0x00 },
|
||||
{ 0x0f16, 0x00 },
|
||||
{ 0x0f17, 0x00 },
|
||||
{ 0x0f18, 0x00 },
|
||||
{ 0x0f19, 0x00 },
|
||||
{ 0x0f1a, 0x00 },
|
||||
{ 0x0f1b, 0x00 },
|
||||
{ 0x0f1c, 0x00 },
|
||||
{ 0x0f1d, 0x00 },
|
||||
{ 0x0f1e, 0x00 },
|
||||
{ 0x0f1f, 0x00 },
|
||||
{ 0x0f20, 0x00 },
|
||||
{ 0x0f21, 0x00 },
|
||||
{ 0x0f22, 0x00 },
|
||||
{ 0x0f23, 0x00 },
|
||||
{ 0x0f24, 0x00 },
|
||||
{ 0x0f25, 0x00 },
|
||||
{ 0x0f26, 0x00 },
|
||||
{ 0x0f27, 0x00 },
|
||||
{ 0x0f30, 0x00 },
|
||||
{ 0x0f31, 0x00 },
|
||||
{ 0x0f32, 0x00 },
|
||||
{ 0x0f33, 0x00 },
|
||||
{ 0x0f34, 0x00 },
|
||||
{ 0x0f35, 0x00 },
|
||||
{ 0x0f36, 0x00 },
|
||||
{ 0x0f37, 0x00 },
|
||||
{ 0x2000, 0x00 },
|
||||
{ 0x2001, 0x00 },
|
||||
{ 0x2002, 0x00 },
|
||||
{ 0x2003, 0x00 },
|
||||
{ 0x2004, 0x00 },
|
||||
{ 0x2005, 0x00 },
|
||||
{ 0x2006, 0x00 },
|
||||
{ 0x2007, 0x00 },
|
||||
{ 0x2008, 0x00 },
|
||||
{ 0x2009, 0x03 },
|
||||
{ 0x200a, 0x00 },
|
||||
{ 0x200b, 0x00 },
|
||||
{ 0x200c, 0x00 },
|
||||
{ 0x200d, 0x00 },
|
||||
{ 0x200e, 0x00 },
|
||||
{ 0x200f, 0x10 },
|
||||
{ 0x2010, 0x00 },
|
||||
{ 0x2011, 0x00 },
|
||||
{ 0x2012, 0x00 },
|
||||
{ 0x2013, 0x00 },
|
||||
{ 0x2014, 0x00 },
|
||||
{ 0x2015, 0x00 },
|
||||
{ 0x2016, 0x00 },
|
||||
{ 0x201a, 0x00 },
|
||||
{ 0x201b, 0x00 },
|
||||
{ 0x201c, 0x00 },
|
||||
{ 0x201d, 0x00 },
|
||||
{ 0x201e, 0x00 },
|
||||
{ 0x201f, 0x00 },
|
||||
{ 0x2020, 0x00 },
|
||||
{ 0x2021, 0x00 },
|
||||
{ 0x2022, 0x00 },
|
||||
{ 0x2023, 0x00 },
|
||||
{ 0x2024, 0x00 },
|
||||
{ 0x2025, 0x01 },
|
||||
{ 0x2026, 0x00 },
|
||||
{ 0x2027, 0x00 },
|
||||
{ 0x2029, 0x00 },
|
||||
{ 0x202a, 0x00 },
|
||||
{ 0x202d, 0x00 },
|
||||
{ 0x202e, 0x00 },
|
||||
{ 0x202f, 0x00 },
|
||||
{ 0x2030, 0x00 },
|
||||
{ 0x2031, 0x00 },
|
||||
{ 0x2032, 0x00 },
|
||||
{ 0x2033, 0x00 },
|
||||
{ 0x2034, 0x00 },
|
||||
{ 0x2200, 0x00 },
|
||||
{ 0x2201, 0x00 },
|
||||
{ 0x2202, 0x00 },
|
||||
{ 0x2203, 0x00 },
|
||||
{ 0x2204, 0x00 },
|
||||
{ 0x2206, 0x00 },
|
||||
{ 0x2207, 0x00 },
|
||||
{ 0x2208, 0x00 },
|
||||
{ 0x2209, 0x00 },
|
||||
{ 0x220a, 0x00 },
|
||||
{ 0x220b, 0x00 },
|
||||
{ 0x220c, 0x00 },
|
||||
{ 0x220d, 0x00 },
|
||||
{ 0x220e, 0x00 },
|
||||
{ 0x220f, 0x00 },
|
||||
{ 0x2210, 0x00 },
|
||||
{ 0x2211, 0x00 },
|
||||
{ 0x2212, 0x00 },
|
||||
{ 0x2220, 0x00 },
|
||||
{ 0x2221, 0x00 },
|
||||
{ 0x2222, 0x00 },
|
||||
{ 0x2223, 0x00 },
|
||||
{ 0x2230, 0x00 },
|
||||
{ 0x2231, 0x0f },
|
||||
{ 0x2232, 0x00 },
|
||||
{ 0x2233, 0x00 },
|
||||
{ 0x2234, 0x00 },
|
||||
{ 0x2235, 0x00 },
|
||||
{ 0x2236, 0x00 },
|
||||
{ 0x2237, 0x00 },
|
||||
{ 0x2238, 0x00 },
|
||||
{ 0x2239, 0x00 },
|
||||
{ 0x22f0, 0x00 },
|
||||
{ 0x22f1, 0x00 },
|
||||
{ 0x22f2, 0x00 },
|
||||
{ 0x22f3, 0x00 },
|
||||
{ 0x3122, 0x02 },
|
||||
{ 0x3123, 0x03 },
|
||||
{ 0x3124, 0x00 },
|
||||
{ 0x3125, 0x01 },
|
||||
{ 0x3607, 0x00 },
|
||||
{ 0x3608, 0x00 },
|
||||
{ 0x3609, 0x00 },
|
||||
{ 0x3610, 0x00 },
|
||||
{ 0x3611, 0x00 },
|
||||
{ 0x3627, 0x00 },
|
||||
{ 0x3712, 0x00 },
|
||||
{ 0x3713, 0x00 },
|
||||
{ 0x3718, 0x00 },
|
||||
{ 0x3719, 0x00 },
|
||||
{ 0x371a, 0x00 },
|
||||
{ 0x371b, 0x00 },
|
||||
{ 0x371d, 0x00 },
|
||||
{ 0x3729, 0x00 },
|
||||
{ 0x385e, 0x00 },
|
||||
{ 0x3859, 0x00 },
|
||||
{ 0x4c12, 0x411111f0 },
|
||||
{ 0x4c13, 0x411111f0 },
|
||||
{ 0x4c1d, 0x411111f0 },
|
||||
{ 0x4c29, 0x411111f0 },
|
||||
{ 0x4d12, 0x411111f0 },
|
||||
{ 0x4d13, 0x411111f0 },
|
||||
{ 0x4d1d, 0x411111f0 },
|
||||
{ 0x4d29, 0x411111f0 },
|
||||
{ 0x4e12, 0x411111f0 },
|
||||
{ 0x4e13, 0x411111f0 },
|
||||
{ 0x4e1d, 0x411111f0 },
|
||||
{ 0x4e29, 0x411111f0 },
|
||||
{ 0x4f12, 0x411111f0 },
|
||||
{ 0x4f13, 0x411111f0 },
|
||||
{ 0x4f1d, 0x411111f0 },
|
||||
{ 0x4f29, 0x411111f0 },
|
||||
{ 0x7207, 0x00 },
|
||||
{ 0x8287, 0x00 },
|
||||
{ 0x7208, 0x00 },
|
||||
{ 0x8288, 0x00 },
|
||||
{ 0x7209, 0x00 },
|
||||
{ 0x8289, 0x00 },
|
||||
{ 0x7227, 0x00 },
|
||||
{ 0x82a7, 0x00 },
|
||||
{ 0x7307, 0x97 },
|
||||
{ 0x8387, 0x97 },
|
||||
{ 0x7308, 0x97 },
|
||||
{ 0x8388, 0x97 },
|
||||
{ 0x7309, 0x97 },
|
||||
{ 0x8389, 0x97 },
|
||||
{ 0x7312, 0x00 },
|
||||
{ 0x8392, 0x00 },
|
||||
{ 0x7313, 0x00 },
|
||||
{ 0x8393, 0x00 },
|
||||
{ 0x7318, 0x00 },
|
||||
{ 0x8398, 0x00 },
|
||||
{ 0x7319, 0x00 },
|
||||
{ 0x8399, 0x00 },
|
||||
{ 0x731a, 0x00 },
|
||||
{ 0x839a, 0x00 },
|
||||
{ 0x731b, 0x00 },
|
||||
{ 0x839b, 0x00 },
|
||||
{ 0x731d, 0x00 },
|
||||
{ 0x839d, 0x00 },
|
||||
{ 0x7327, 0x97 },
|
||||
{ 0x83a7, 0x97 },
|
||||
{ 0x7329, 0x00 },
|
||||
{ 0x83a9, 0x00 },
|
||||
{ 0x752039, 0xa500 },
|
||||
};
|
||||
|
||||
#endif /* __RT715_H__ */
|
872
sound/soc/codecs/rt715.c
Normal file
872
sound/soc/codecs/rt715.c
Normal file
@ -0,0 +1,872 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* rt715.c -- rt715 ALSA SoC audio driver
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*
|
||||
* ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/hda_verbs.h>
|
||||
|
||||
#include "rt715.h"
|
||||
|
||||
static int rt715_index_write(struct regmap *regmap, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
int ret;
|
||||
unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg;
|
||||
|
||||
ret = regmap_write(regmap, addr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to set private value: %08x <= %04x %d\n", ret,
|
||||
addr, value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h,
|
||||
unsigned int addr_l, unsigned int val_h,
|
||||
unsigned int *r_val, unsigned int *l_val)
|
||||
{
|
||||
int ret;
|
||||
/* R Channel */
|
||||
*r_val = (val_h << 8);
|
||||
ret = regmap_read(rt715->regmap, addr_l, r_val);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to get R channel gain.\n");
|
||||
|
||||
/* L Channel */
|
||||
val_h |= 0x20;
|
||||
*l_val = (val_h << 8);
|
||||
ret = regmap_read(rt715->regmap, addr_h, l_val);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to get L channel gain.\n");
|
||||
}
|
||||
|
||||
/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
|
||||
static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(component);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
|
||||
unsigned int read_ll, read_rl;
|
||||
int i;
|
||||
|
||||
/* Can't use update bit function, so read the original value first */
|
||||
addr_h = mc->reg;
|
||||
addr_l = mc->rreg;
|
||||
if (mc->shift == RT715_DIR_OUT_SFT) /* output */
|
||||
val_h = 0x80;
|
||||
else /* input */
|
||||
val_h = 0x0;
|
||||
|
||||
rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
|
||||
|
||||
/* L Channel */
|
||||
if (mc->invert) {
|
||||
/* for mute */
|
||||
val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7;
|
||||
/* keep gain */
|
||||
read_ll = read_ll & 0x7f;
|
||||
val_ll |= read_ll;
|
||||
} else {
|
||||
/* for gain */
|
||||
val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
|
||||
if (val_ll > mc->max)
|
||||
val_ll = mc->max;
|
||||
/* keep mute status */
|
||||
read_ll = read_ll & 0x80;
|
||||
val_ll |= read_ll;
|
||||
}
|
||||
|
||||
/* R Channel */
|
||||
if (mc->invert) {
|
||||
regmap_write(rt715->regmap,
|
||||
RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
|
||||
/* for mute */
|
||||
val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7;
|
||||
/* keep gain */
|
||||
read_rl = read_rl & 0x7f;
|
||||
val_lr |= read_rl;
|
||||
} else {
|
||||
/* for gain */
|
||||
val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
|
||||
if (val_lr > mc->max)
|
||||
val_lr = mc->max;
|
||||
/* keep mute status */
|
||||
read_rl = read_rl & 0x80;
|
||||
val_lr |= read_rl;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) { /* retry 3 times at most */
|
||||
|
||||
if (val_ll == val_lr) {
|
||||
/* Set both L/R channels at the same time */
|
||||
val_h = (1 << mc->shift) | (3 << 4);
|
||||
regmap_write(rt715->regmap, addr_h,
|
||||
(val_h << 8 | val_ll));
|
||||
regmap_write(rt715->regmap, addr_l,
|
||||
(val_h << 8 | val_ll));
|
||||
} else {
|
||||
/* Lch*/
|
||||
val_h = (1 << mc->shift) | (1 << 5);
|
||||
regmap_write(rt715->regmap, addr_h,
|
||||
(val_h << 8 | val_ll));
|
||||
/* Rch */
|
||||
val_h = (1 << mc->shift) | (1 << 4);
|
||||
regmap_write(rt715->regmap, addr_l,
|
||||
(val_h << 8 | val_lr));
|
||||
}
|
||||
/* check result */
|
||||
if (mc->shift == RT715_DIR_OUT_SFT) /* output */
|
||||
val_h = 0x80;
|
||||
else /* input */
|
||||
val_h = 0x0;
|
||||
|
||||
rt715_get_gain(rt715, addr_h, addr_l, val_h,
|
||||
&read_rl, &read_ll);
|
||||
if (read_rl == val_lr && read_ll == val_ll)
|
||||
break;
|
||||
}
|
||||
/* D0:power on state, D3: power saving mode */
|
||||
if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
|
||||
regmap_write(rt715->regmap,
|
||||
RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int addr_h, addr_l, val_h;
|
||||
unsigned int read_ll, read_rl;
|
||||
|
||||
addr_h = mc->reg;
|
||||
addr_l = mc->rreg;
|
||||
if (mc->shift == RT715_DIR_OUT_SFT) /* output */
|
||||
val_h = 0x80;
|
||||
else /* input */
|
||||
val_h = 0x0;
|
||||
|
||||
rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
|
||||
|
||||
if (mc->invert) {
|
||||
/* for mute status */
|
||||
read_ll = !((read_ll & 0x80) >> RT715_MUTE_SFT);
|
||||
read_rl = !((read_rl & 0x80) >> RT715_MUTE_SFT);
|
||||
} else {
|
||||
/* for gain */
|
||||
read_ll = read_ll & 0x7f;
|
||||
read_rl = read_rl & 0x7f;
|
||||
}
|
||||
ucontrol->value.integer.value[0] = read_ll;
|
||||
ucontrol->value.integer.value[1] = read_rl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
|
||||
|
||||
#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
|
||||
xmax, xinvert) }
|
||||
|
||||
static const struct snd_kcontrol_new rt715_snd_controls[] = {
|
||||
/* Capture switch */
|
||||
SOC_DOUBLE_R_EXT("ADC 07 Capture Switch", RT715_SET_GAIN_MIC_ADC_H,
|
||||
RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 1, 1,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put),
|
||||
SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT715_SET_GAIN_LINE_ADC_H,
|
||||
RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 1, 1,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put),
|
||||
SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT715_SET_GAIN_MIX_ADC_H,
|
||||
RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 1, 1,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put),
|
||||
SOC_DOUBLE_R_EXT("ADC 27 Capture Switch", RT715_SET_GAIN_MIX_ADC2_H,
|
||||
RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 1, 1,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put),
|
||||
/* Volume Control */
|
||||
SOC_DOUBLE_R_EXT_TLV("ADC 07 Capture Volume", RT715_SET_GAIN_MIC_ADC_H,
|
||||
RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT715_SET_GAIN_LINE_ADC_H,
|
||||
RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT715_SET_GAIN_MIX_ADC_H,
|
||||
RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("ADC 27 Capture Volume", RT715_SET_GAIN_MIX_ADC2_H,
|
||||
RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 0x3f, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
in_vol_tlv),
|
||||
/* MIC Boost Control */
|
||||
SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H,
|
||||
RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H,
|
||||
RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H,
|
||||
RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H,
|
||||
RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H,
|
||||
RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H,
|
||||
RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H,
|
||||
RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H,
|
||||
RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0,
|
||||
rt715_set_amp_gain_get, rt715_set_amp_gain_put,
|
||||
mic_vol_tlv),
|
||||
};
|
||||
|
||||
static int rt715_mux_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int reg, val;
|
||||
int ret;
|
||||
|
||||
/* nid = e->reg, vid = 0xf01 */
|
||||
reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
|
||||
ret = regmap_read(rt715->regmap, reg, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "%s: sdw read failed: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first two indices of ADC Mux 24/25 are routed to the same
|
||||
* hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
|
||||
* To have a unique set of inputs, we skip the index1 of the muxes.
|
||||
*/
|
||||
if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0))
|
||||
val -= 1;
|
||||
ucontrol->value.enumerated.item[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_mux_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int *item = ucontrol->value.enumerated.item;
|
||||
unsigned int val, val2 = 0, change, reg;
|
||||
int ret;
|
||||
|
||||
if (item[0] >= e->items)
|
||||
return -EINVAL;
|
||||
|
||||
/* Verb ID = 0x701h, nid = e->reg */
|
||||
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
|
||||
|
||||
reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
|
||||
ret = regmap_read(rt715->regmap, reg, &val2);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "%s: sdw read failed: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val == val2)
|
||||
change = 0;
|
||||
else
|
||||
change = 1;
|
||||
|
||||
if (change) {
|
||||
reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
|
||||
regmap_write(rt715->regmap, reg, val);
|
||||
}
|
||||
|
||||
snd_soc_dapm_mux_update_power(dapm, kcontrol,
|
||||
item[0], e, NULL);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
static const char * const adc_22_23_mux_text[] = {
|
||||
"MIC1",
|
||||
"MIC2",
|
||||
"LINE1",
|
||||
"LINE2",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
/*
|
||||
* Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
|
||||
* 1 will be connected to the same dmic source, therefore we skip index 1 to
|
||||
* avoid misunderstanding on usage of dapm routing.
|
||||
*/
|
||||
static const unsigned int rt715_adc_24_25_values[] = {
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
};
|
||||
|
||||
static const char * const adc_24_mux_text[] = {
|
||||
"MIC2",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
static const char * const adc_25_mux_text[] = {
|
||||
"MIC1",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
|
||||
RT715_MUX_IN3, 0, 0xf,
|
||||
adc_24_mux_text, rt715_adc_24_25_values);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
|
||||
RT715_MUX_IN4, 0, 0xf,
|
||||
adc_25_mux_text, rt715_adc_24_25_values);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc22_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc23_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc24_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc25_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("DMIC1"),
|
||||
SND_SOC_DAPM_INPUT("DMIC2"),
|
||||
SND_SOC_DAPM_INPUT("DMIC3"),
|
||||
SND_SOC_DAPM_INPUT("DMIC4"),
|
||||
SND_SOC_DAPM_INPUT("MIC1"),
|
||||
SND_SOC_DAPM_INPUT("MIC2"),
|
||||
SND_SOC_DAPM_INPUT("LINE1"),
|
||||
SND_SOC_DAPM_INPUT("LINE2"),
|
||||
SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0),
|
||||
SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc22_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc23_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc24_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc25_mux),
|
||||
SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt715_audio_map[] = {
|
||||
{"DP6TX", NULL, "ADC 09"},
|
||||
{"DP6TX", NULL, "ADC 08"},
|
||||
{"DP4TX", NULL, "ADC 07"},
|
||||
{"DP4TX", NULL, "ADC 27"},
|
||||
{"ADC 09", NULL, "ADC 22 Mux"},
|
||||
{"ADC 08", NULL, "ADC 23 Mux"},
|
||||
{"ADC 07", NULL, "ADC 24 Mux"},
|
||||
{"ADC 27", NULL, "ADC 25 Mux"},
|
||||
{"ADC 22 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 22 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 22 Mux", "LINE1", "LINE1"},
|
||||
{"ADC 22 Mux", "LINE2", "LINE2"},
|
||||
{"ADC 22 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 22 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 22 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 22 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 23 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 23 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 23 Mux", "LINE1", "LINE1"},
|
||||
{"ADC 23 Mux", "LINE2", "LINE2"},
|
||||
{"ADC 23 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 23 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 23 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 23 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 24 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 24 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 24 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 24 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 24 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 25 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 25 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 25 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 25 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 25 Mux", "DMIC4", "DMIC4"},
|
||||
};
|
||||
|
||||
static int rt715_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(component);
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
|
||||
regmap_write(rt715->regmap,
|
||||
RT715_SET_AUDIO_POWER_STATE,
|
||||
AC_PWRST_D0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
regmap_write(rt715->regmap,
|
||||
RT715_SET_AUDIO_POWER_STATE,
|
||||
AC_PWRST_D3);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dapm->bias_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver soc_codec_dev_rt715 = {
|
||||
.set_bias_level = rt715_set_bias_level,
|
||||
.controls = rt715_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(rt715_snd_controls),
|
||||
.dapm_widgets = rt715_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
|
||||
.dapm_routes = rt715_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
|
||||
};
|
||||
|
||||
static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
|
||||
int direction)
|
||||
{
|
||||
|
||||
struct sdw_stream_data *stream;
|
||||
|
||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (!stream)
|
||||
return -ENOMEM;
|
||||
|
||||
stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream;
|
||||
|
||||
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dai->playback_dma_data = stream;
|
||||
else
|
||||
dai->capture_dma_data = stream;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rt715_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
|
||||
{
|
||||
struct sdw_stream_data *stream;
|
||||
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
kfree(stream);
|
||||
}
|
||||
|
||||
static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_data *stream;
|
||||
int retval, port, num_channels;
|
||||
unsigned int val = 0;
|
||||
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rt715->slave)
|
||||
return -EINVAL;
|
||||
|
||||
switch (dai->id) {
|
||||
case RT715_AIF1:
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
port = 6;
|
||||
rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500);
|
||||
break;
|
||||
case RT715_AIF2:
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
port = 4;
|
||||
rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Invalid DAI id %d\n", dai->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = (1 << (num_channels)) - 1;
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt715->slave, &stream_config,
|
||||
&port_config, 1, stream->sdw_stream);
|
||||
if (retval) {
|
||||
dev_err(dai->dev, "Unable to configure port\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
switch (params_rate(params)) {
|
||||
/* bit 14 0:48K 1:44.1K */
|
||||
/* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */
|
||||
case 44100:
|
||||
val |= 0x40 << 8;
|
||||
break;
|
||||
case 48000:
|
||||
val |= 0x0 << 8;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Unsupported sample rate %d\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (params_channels(params) <= 16) {
|
||||
/* bit 3:0 Number of Channel */
|
||||
val |= (params_channels(params) - 1);
|
||||
} else {
|
||||
dev_err(component->dev, "Unsupported channels %d\n",
|
||||
params_channels(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (params_width(params)) {
|
||||
/* bit 6:4 Bits per Sample */
|
||||
case 8:
|
||||
break;
|
||||
case 16:
|
||||
val |= (0x1 << 4);
|
||||
break;
|
||||
case 20:
|
||||
val |= (0x2 << 4);
|
||||
break;
|
||||
case 24:
|
||||
val |= (0x3 << 4);
|
||||
break;
|
||||
case 32:
|
||||
val |= (0x4 << 4);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val);
|
||||
regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val);
|
||||
regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val);
|
||||
regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_data *stream =
|
||||
snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!rt715->slave)
|
||||
return -EINVAL;
|
||||
|
||||
sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
|
||||
#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
|
||||
|
||||
static struct snd_soc_dai_ops rt715_ops = {
|
||||
.hw_params = rt715_pcm_hw_params,
|
||||
.hw_free = rt715_pcm_hw_free,
|
||||
.set_sdw_stream = rt715_set_sdw_stream,
|
||||
.shutdown = rt715_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rt715_dai[] = {
|
||||
{
|
||||
.name = "rt715-aif1",
|
||||
.id = RT715_AIF1,
|
||||
.capture = {
|
||||
.stream_name = "DP6 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RT715_STEREO_RATES,
|
||||
.formats = RT715_FORMATS,
|
||||
},
|
||||
.ops = &rt715_ops,
|
||||
},
|
||||
{
|
||||
.name = "rt715-aif2",
|
||||
.id = RT715_AIF2,
|
||||
.capture = {
|
||||
.stream_name = "DP4 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RT715_STEREO_RATES,
|
||||
.formats = RT715_FORMATS,
|
||||
},
|
||||
.ops = &rt715_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* Bus clock frequency */
|
||||
#define RT715_CLK_FREQ_9600000HZ 9600000
|
||||
#define RT715_CLK_FREQ_12000000HZ 12000000
|
||||
#define RT715_CLK_FREQ_6000000HZ 6000000
|
||||
#define RT715_CLK_FREQ_4800000HZ 4800000
|
||||
#define RT715_CLK_FREQ_2400000HZ 2400000
|
||||
#define RT715_CLK_FREQ_12288000HZ 12288000
|
||||
|
||||
int rt715_clock_config(struct device *dev)
|
||||
{
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned int clk_freq, value;
|
||||
|
||||
clk_freq = (rt715->params.curr_dr_freq >> 1);
|
||||
|
||||
switch (clk_freq) {
|
||||
case RT715_CLK_FREQ_12000000HZ:
|
||||
value = 0x0;
|
||||
break;
|
||||
case RT715_CLK_FREQ_6000000HZ:
|
||||
value = 0x1;
|
||||
break;
|
||||
case RT715_CLK_FREQ_9600000HZ:
|
||||
value = 0x2;
|
||||
break;
|
||||
case RT715_CLK_FREQ_4800000HZ:
|
||||
value = 0x3;
|
||||
break;
|
||||
case RT715_CLK_FREQ_2400000HZ:
|
||||
value = 0x4;
|
||||
break;
|
||||
case RT715_CLK_FREQ_12288000HZ:
|
||||
value = 0x5;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(rt715->regmap, 0xe0, value);
|
||||
regmap_write(rt715->regmap, 0xf0, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rt715_init(struct device *dev, struct regmap *sdw_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt715_priv *rt715;
|
||||
int ret;
|
||||
|
||||
rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
|
||||
if (!rt715)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, rt715);
|
||||
rt715->slave = slave;
|
||||
rt715->regmap = regmap;
|
||||
rt715->sdw_regmap = sdw_regmap;
|
||||
|
||||
/*
|
||||
* Mark hw_init to false
|
||||
* HW init will be performed when device reports present
|
||||
*/
|
||||
rt715->hw_init = false;
|
||||
rt715->first_hw_init = false;
|
||||
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&soc_codec_dev_rt715,
|
||||
rt715_dai,
|
||||
ARRAY_SIZE(rt715_dai));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rt715_io_init(struct device *dev, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt715_priv *rt715 = dev_get_drvdata(dev);
|
||||
|
||||
if (rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* PM runtime is only enabled when a Slave reports as Attached
|
||||
*/
|
||||
if (!rt715->first_hw_init) {
|
||||
/* set autosuspend parameters */
|
||||
pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
|
||||
pm_runtime_use_autosuspend(&slave->dev);
|
||||
|
||||
/* update count of parent 'active' children */
|
||||
pm_runtime_set_active(&slave->dev);
|
||||
|
||||
/* make sure the device does not suspend immediately */
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
|
||||
pm_runtime_enable(&slave->dev);
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&slave->dev);
|
||||
|
||||
/* Mute nid=08h/09h */
|
||||
regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080);
|
||||
regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080);
|
||||
/* Mute nid=07h/27h */
|
||||
regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080);
|
||||
regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080);
|
||||
|
||||
/* Set Pin Widget */
|
||||
regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20);
|
||||
regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20);
|
||||
regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20);
|
||||
regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20);
|
||||
/* Set Converter Stream */
|
||||
regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10);
|
||||
regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10);
|
||||
regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10);
|
||||
regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10);
|
||||
/* Set Configuration Default */
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1);
|
||||
regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81);
|
||||
|
||||
/* Finish Initial Settings, set power to D3 */
|
||||
regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
|
||||
|
||||
if (rt715->first_hw_init)
|
||||
regcache_mark_dirty(rt715->regmap);
|
||||
else
|
||||
rt715->first_hw_init = true;
|
||||
|
||||
/* Mark Slave initialization complete */
|
||||
rt715->hw_init = true;
|
||||
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
pm_runtime_put_autosuspend(&slave->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("ASoC rt715 driver");
|
||||
MODULE_DESCRIPTION("ASoC rt715 driver SDW");
|
||||
MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
221
sound/soc/codecs/rt715.h
Normal file
221
sound/soc/codecs/rt715.h
Normal file
@ -0,0 +1,221 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* rt715.h -- RT715 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2019 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT715_H__
|
||||
#define __RT715_H__
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct rt715_priv {
|
||||
struct regmap *regmap;
|
||||
struct regmap *sdw_regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct sdw_slave *slave;
|
||||
int dbg_nid;
|
||||
int dbg_vid;
|
||||
int dbg_payload;
|
||||
enum sdw_slave_status status;
|
||||
struct sdw_bus_params params;
|
||||
bool hw_init;
|
||||
bool first_hw_init;
|
||||
};
|
||||
|
||||
struct sdw_stream_data {
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
};
|
||||
|
||||
/* NID */
|
||||
#define RT715_AUDIO_FUNCTION_GROUP 0x01
|
||||
#define RT715_MIC_ADC 0x07
|
||||
#define RT715_LINE_ADC 0x08
|
||||
#define RT715_MIX_ADC 0x09
|
||||
#define RT715_DMIC1 0x12
|
||||
#define RT715_DMIC2 0x13
|
||||
#define RT715_MIC1 0x18
|
||||
#define RT715_MIC2 0x19
|
||||
#define RT715_LINE1 0x1a
|
||||
#define RT715_LINE2 0x1b
|
||||
#define RT715_DMIC3 0x1d
|
||||
#define RT715_DMIC4 0x29
|
||||
#define RT715_VENDOR_REGISTERS 0x20
|
||||
#define RT715_MUX_IN1 0x22
|
||||
#define RT715_MUX_IN2 0x23
|
||||
#define RT715_MUX_IN3 0x24
|
||||
#define RT715_MUX_IN4 0x25
|
||||
#define RT715_MIX_ADC2 0x27
|
||||
#define RT715_INLINE_CMD 0x55
|
||||
|
||||
/* Index (NID:20h) */
|
||||
#define RT715_SDW_INPUT_SEL 0x39
|
||||
#define RT715_EXT_DMIC_CLK_CTRL2 0x54
|
||||
|
||||
/* Verb */
|
||||
#define RT715_VERB_SET_CONNECT_SEL 0x3100
|
||||
#define RT715_VERB_GET_CONNECT_SEL 0xb100
|
||||
#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00
|
||||
#define RT715_VERB_SET_POWER_STATE 0x3500
|
||||
#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600
|
||||
#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700
|
||||
#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00
|
||||
#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00
|
||||
#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00
|
||||
#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00
|
||||
#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800
|
||||
#define RT715_SET_AMP_GAIN_MUTE_H 0x7300
|
||||
#define RT715_SET_AMP_GAIN_MUTE_L 0x8380
|
||||
#define RT715_READ_HDA_3 0x2012
|
||||
#define RT715_READ_HDA_2 0x2013
|
||||
#define RT715_READ_HDA_1 0x2014
|
||||
#define RT715_READ_HDA_0 0x2015
|
||||
#define RT715_PRIV_INDEX_W_H 0x7520
|
||||
#define RT715_PRIV_INDEX_W_L 0x85a0
|
||||
#define RT715_PRIV_DATA_W_H 0x7420
|
||||
#define RT715_PRIV_DATA_W_L 0x84a0
|
||||
#define RT715_PRIV_INDEX_R_H 0x9d20
|
||||
#define RT715_PRIV_INDEX_R_L 0xada0
|
||||
#define RT715_PRIV_DATA_R_H 0x9c20
|
||||
#define RT715_PRIV_DATA_R_L 0xaca0
|
||||
#define RT715_MIC_ADC_FORMAT_H 0x7207
|
||||
#define RT715_MIC_ADC_FORMAT_L 0x8287
|
||||
#define RT715_MIC_LINE_FORMAT_H 0x7208
|
||||
#define RT715_MIC_LINE_FORMAT_L 0x8288
|
||||
#define RT715_MIX_ADC_FORMAT_H 0x7209
|
||||
#define RT715_MIX_ADC_FORMAT_L 0x8289
|
||||
#define RT715_MIX_ADC2_FORMAT_H 0x7227
|
||||
#define RT715_MIX_ADC2_FORMAT_L 0x82a7
|
||||
#define RT715_FUNC_RESET 0xff01
|
||||
|
||||
#define RT715_SET_AUDIO_POWER_STATE\
|
||||
(RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP)
|
||||
#define RT715_SET_PIN_DMIC1\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1)
|
||||
#define RT715_SET_PIN_DMIC2\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2)
|
||||
#define RT715_SET_PIN_DMIC3\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3)
|
||||
#define RT715_SET_PIN_DMIC4\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4)
|
||||
#define RT715_SET_PIN_MIC1\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1)
|
||||
#define RT715_SET_PIN_MIC2\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2)
|
||||
#define RT715_SET_PIN_LINE1\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1)
|
||||
#define RT715_SET_PIN_LINE2\
|
||||
(RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2)
|
||||
#define RT715_SET_MIC1_UNSOLICITED_ENABLE\
|
||||
(RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1)
|
||||
#define RT715_SET_MIC2_UNSOLICITED_ENABLE\
|
||||
(RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2)
|
||||
#define RT715_SET_STREAMID_MIC_ADC\
|
||||
(RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC)
|
||||
#define RT715_SET_STREAMID_LINE_ADC\
|
||||
(RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC)
|
||||
#define RT715_SET_STREAMID_MIX_ADC\
|
||||
(RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC)
|
||||
#define RT715_SET_STREAMID_MIX_ADC2\
|
||||
(RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2)
|
||||
#define RT715_SET_GAIN_MIC_ADC_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC)
|
||||
#define RT715_SET_GAIN_MIC_ADC_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC)
|
||||
#define RT715_SET_GAIN_LINE_ADC_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC)
|
||||
#define RT715_SET_GAIN_LINE_ADC_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC)
|
||||
#define RT715_SET_GAIN_MIX_ADC_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC)
|
||||
#define RT715_SET_GAIN_MIX_ADC_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC)
|
||||
#define RT715_SET_GAIN_MIX_ADC2_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2)
|
||||
#define RT715_SET_GAIN_MIX_ADC2_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2)
|
||||
#define RT715_SET_GAIN_DMIC1_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1)
|
||||
#define RT715_SET_GAIN_DMIC1_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1)
|
||||
#define RT715_SET_GAIN_DMIC2_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2)
|
||||
#define RT715_SET_GAIN_DMIC2_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2)
|
||||
#define RT715_SET_GAIN_DMIC3_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3)
|
||||
#define RT715_SET_GAIN_DMIC3_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3)
|
||||
#define RT715_SET_GAIN_DMIC4_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4)
|
||||
#define RT715_SET_GAIN_DMIC4_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4)
|
||||
#define RT715_SET_GAIN_MIC1_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1)
|
||||
#define RT715_SET_GAIN_MIC1_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1)
|
||||
#define RT715_SET_GAIN_MIC2_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2)
|
||||
#define RT715_SET_GAIN_MIC2_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2)
|
||||
#define RT715_SET_GAIN_LINE1_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1)
|
||||
#define RT715_SET_GAIN_LINE1_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1)
|
||||
#define RT715_SET_GAIN_LINE2_L\
|
||||
(RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2)
|
||||
#define RT715_SET_GAIN_LINE2_H\
|
||||
(RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2)
|
||||
#define RT715_SET_DMIC1_CONFIG_DEFAULT1\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1)
|
||||
#define RT715_SET_DMIC2_CONFIG_DEFAULT1\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2)
|
||||
#define RT715_SET_DMIC1_CONFIG_DEFAULT2\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1)
|
||||
#define RT715_SET_DMIC2_CONFIG_DEFAULT2\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2)
|
||||
#define RT715_SET_DMIC1_CONFIG_DEFAULT3\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1)
|
||||
#define RT715_SET_DMIC2_CONFIG_DEFAULT3\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2)
|
||||
#define RT715_SET_DMIC1_CONFIG_DEFAULT4\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1)
|
||||
#define RT715_SET_DMIC2_CONFIG_DEFAULT4\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2)
|
||||
#define RT715_SET_DMIC3_CONFIG_DEFAULT1\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3)
|
||||
#define RT715_SET_DMIC4_CONFIG_DEFAULT1\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4)
|
||||
#define RT715_SET_DMIC3_CONFIG_DEFAULT2\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3)
|
||||
#define RT715_SET_DMIC4_CONFIG_DEFAULT2\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4)
|
||||
#define RT715_SET_DMIC3_CONFIG_DEFAULT3\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3)
|
||||
#define RT715_SET_DMIC4_CONFIG_DEFAULT3\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4)
|
||||
#define RT715_SET_DMIC3_CONFIG_DEFAULT4\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3)
|
||||
#define RT715_SET_DMIC4_CONFIG_DEFAULT4\
|
||||
(RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4)
|
||||
|
||||
#define RT715_MUTE_SFT 7
|
||||
#define RT715_DIR_IN_SFT 6
|
||||
#define RT715_DIR_OUT_SFT 7
|
||||
|
||||
enum {
|
||||
RT715_AIF1,
|
||||
RT715_AIF2,
|
||||
RT715_AIFS,
|
||||
};
|
||||
|
||||
int rt715_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt715_init(struct device *dev, struct regmap *sdw_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave);
|
||||
|
||||
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
|
||||
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
|
||||
unsigned int *sdw_addr_l, unsigned int *sdw_data_l);
|
||||
int rt715_clock_config(struct device *dev);
|
||||
#endif /* __RT715_H__ */
|
@ -1344,7 +1344,8 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component)
|
||||
* if vddio == vdda the source of charge pump should be
|
||||
* assigned manually to VDDIO
|
||||
*/
|
||||
if (vddio == vdda) {
|
||||
if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer,
|
||||
sgtl5000->supplies[VDDIO].consumer)) {
|
||||
lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
|
||||
lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
|
||||
SGTL5000_VDDC_MAN_ASSN_SHIFT;
|
||||
@ -1513,6 +1514,13 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
/* return dai id 0, whatever the endpoint index */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver sgtl5000_driver = {
|
||||
.probe = sgtl5000_probe,
|
||||
.set_bias_level = sgtl5000_set_bias_level,
|
||||
@ -1522,6 +1530,7 @@ static const struct snd_soc_component_driver sgtl5000_driver = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
|
||||
.dapm_routes = sgtl5000_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
|
||||
.of_xlate_dai_id = sgtl5000_of_xlate_dai_id,
|
||||
.suspend_bias_off = 1,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
|
@ -262,6 +262,25 @@ static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2,
|
||||
static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4,
|
||||
mic_select_text);
|
||||
|
||||
static const char * const hp_poweron_time_text[] = {
|
||||
"0us", "15.3us", "153us", "1.53ms", "15.3ms", "76.2ms",
|
||||
"153ms", "304ms", "610ms", "1.22s", "3.04s", "6.1s" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(hp_poweron_time_enum, AIC31XX_HPPOP, 3,
|
||||
hp_poweron_time_text);
|
||||
|
||||
static const char * const hp_rampup_step_text[] = {
|
||||
"0ms", "0.98ms", "1.95ms", "3.9ms" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(hp_rampup_step_enum, AIC31XX_HPPOP, 1,
|
||||
hp_rampup_step_text);
|
||||
|
||||
static const char * const vol_soft_step_mode_text[] = {
|
||||
"fast", "slow", "disabled" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(vol_soft_step_mode_enum, AIC31XX_DACSETUP, 0,
|
||||
vol_soft_step_mode_text);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0);
|
||||
@ -285,6 +304,16 @@ static const struct snd_kcontrol_new common31xx_snd_controls[] = {
|
||||
|
||||
SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
|
||||
AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
|
||||
|
||||
/* HP de-pop control: apply power not immediately but via ramp
|
||||
* function with these psarameters. Note that power up sequence
|
||||
* has to wait for this to complete; this is implemented by
|
||||
* polling HP driver status in aic31xx_dapm_power_event()
|
||||
*/
|
||||
SOC_ENUM("HP Output Driver Power-On time", hp_poweron_time_enum),
|
||||
SOC_ENUM("HP Output Driver Ramp-up step", hp_rampup_step_enum),
|
||||
|
||||
SOC_ENUM("Volume Soft Stepping", vol_soft_step_mode_enum),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
|
||||
@ -357,6 +386,7 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
|
||||
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
||||
unsigned int reg = AIC31XX_DACFLAG1;
|
||||
unsigned int mask;
|
||||
unsigned int timeout = 500 * USEC_PER_MSEC;
|
||||
|
||||
switch (WIDGET_BIT(w->reg, w->shift)) {
|
||||
case WIDGET_BIT(AIC31XX_DACSETUP, 7):
|
||||
@ -367,9 +397,13 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
case WIDGET_BIT(AIC31XX_HPDRIVER, 7):
|
||||
mask = AIC31XX_HPLDRVPWRSTATUS_MASK;
|
||||
if (event == SND_SOC_DAPM_POST_PMU)
|
||||
timeout = 7 * USEC_PER_SEC;
|
||||
break;
|
||||
case WIDGET_BIT(AIC31XX_HPDRIVER, 6):
|
||||
mask = AIC31XX_HPRDRVPWRSTATUS_MASK;
|
||||
if (event == SND_SOC_DAPM_POST_PMU)
|
||||
timeout = 7 * USEC_PER_SEC;
|
||||
break;
|
||||
case WIDGET_BIT(AIC31XX_SPKAMP, 7):
|
||||
mask = AIC31XX_SPLDRVPWRSTATUS_MASK;
|
||||
@ -389,9 +423,11 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
return aic31xx_wait_bits(aic31xx, reg, mask, mask, 5000, 100);
|
||||
return aic31xx_wait_bits(aic31xx, reg, mask, mask,
|
||||
5000, timeout / 5000);
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
return aic31xx_wait_bits(aic31xx, reg, mask, 0, 5000, 100);
|
||||
return aic31xx_wait_bits(aic31xx, reg, mask, 0,
|
||||
5000, timeout / 5000);
|
||||
default:
|
||||
dev_dbg(component->dev,
|
||||
"Unhandled dapm widget event %d from %s\n",
|
||||
|
@ -218,9 +218,6 @@ struct aic31xx_pdata {
|
||||
#define AIC31XX_GPIO1_ADC_MOD_CLK 0x10
|
||||
#define AIC31XX_GPIO1_SDOUT 0x11
|
||||
|
||||
/* AIC31XX_DACSETUP */
|
||||
#define AIC31XX_SOFTSTEP_MASK GENMASK(1, 0)
|
||||
|
||||
/* AIC31XX_DACMUTE */
|
||||
#define AIC31XX_DACMUTE_MASK GENMASK(3, 2)
|
||||
|
||||
|
5084
sound/soc/codecs/wcd934x.c
Normal file
5084
sound/soc/codecs/wcd934x.c
Normal file
File diff suppressed because it is too large
Load Diff
1185
sound/soc/codecs/wsa881x.c
Normal file
1185
sound/soc/codecs/wsa881x.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -390,10 +390,6 @@ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
|
||||
.set_fmt = dw_i2s_set_fmt,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver dw_i2s_component = {
|
||||
.name = "dw-i2s",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dw_i2s_runtime_suspend(struct device *dev)
|
||||
{
|
||||
@ -413,26 +409,30 @@ static int dw_i2s_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2s_suspend(struct snd_soc_dai *dai)
|
||||
static int dw_i2s_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (dev->capability & DW_I2S_MASTER)
|
||||
clk_disable(dev->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2s_resume(struct snd_soc_dai *dai)
|
||||
static int dw_i2s_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_dai *dai;
|
||||
|
||||
if (dev->capability & DW_I2S_MASTER)
|
||||
clk_enable(dev->clk);
|
||||
|
||||
if (dai->playback_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (dai->capture_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
|
||||
for_each_component_dais(component, dai) {
|
||||
if (dai->playback_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (dai->capture_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -441,6 +441,12 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
|
||||
#define dw_i2s_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_soc_component_driver dw_i2s_component = {
|
||||
.name = "dw-i2s",
|
||||
.suspend = dw_i2s_suspend,
|
||||
.resume = dw_i2s_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
* The following tables allow a direct lookup of various parameters
|
||||
* defined in the I2S block's configuration in terms of sound system
|
||||
@ -629,8 +635,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
dw_i2s_dai->ops = &dw_i2s_dai_ops;
|
||||
dw_i2s_dai->suspend = dw_i2s_suspend;
|
||||
dw_i2s_dai->resume = dw_i2s_resume;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -162,7 +162,6 @@ static int dw_pcm_hw_params(struct snd_soc_component *component,
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct dw_i2s_dev *dev = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
switch (params_channels(hw_params)) {
|
||||
case 2:
|
||||
@ -187,18 +186,7 @@ static int dw_pcm_hw_params(struct snd_soc_component *component,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_trigger(struct snd_soc_component *component,
|
||||
@ -256,28 +244,19 @@ static int dw_pcm_new(struct snd_soc_component *component,
|
||||
{
|
||||
size_t size = dw_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
NULL, size, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver dw_pcm_component = {
|
||||
.open = dw_pcm_open,
|
||||
.close = dw_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = dw_pcm_hw_params,
|
||||
.hw_free = dw_pcm_hw_free,
|
||||
.trigger = dw_pcm_trigger,
|
||||
.pointer = dw_pcm_pointer,
|
||||
.pcm_construct = dw_pcm_new,
|
||||
.pcm_destruct = dw_pcm_free,
|
||||
};
|
||||
|
||||
int dw_pcm_register(struct platform_device *pdev)
|
||||
|
@ -256,7 +256,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
|
||||
unsigned int pll_out;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
|
||||
codec_dai = rtd->codec_dai;
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
@ -41,26 +41,65 @@ static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
|
||||
* The following tables map the relationship between asrc_inclk/asrc_outclk in
|
||||
* fsl_asrc.h and the registers of ASRCSR
|
||||
*/
|
||||
static unsigned char input_clk_map_imx35[] = {
|
||||
static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx35[] = {
|
||||
static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
};
|
||||
|
||||
/* i.MX53 uses the same map for input and output */
|
||||
static unsigned char input_clk_map_imx53[] = {
|
||||
static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
|
||||
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
|
||||
0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx53[] = {
|
||||
static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
|
||||
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
|
||||
0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
};
|
||||
|
||||
static unsigned char *clk_map[2];
|
||||
/**
|
||||
* i.MX8QM/i.MX8QXP uses the same map for input and output.
|
||||
* clk_map_imx8qm[0] is for i.MX8QM asrc0
|
||||
* clk_map_imx8qm[1] is for i.MX8QM asrc1
|
||||
* clk_map_imx8qxp[0] is for i.MX8QXP asrc0
|
||||
* clk_map_imx8qxp[1] is for i.MX8QXP asrc1
|
||||
*/
|
||||
static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
|
||||
{
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
},
|
||||
{
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
|
||||
{
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
},
|
||||
{
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the pre-processing and post-processing options
|
||||
@ -353,8 +392,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
|
||||
}
|
||||
|
||||
/* Validate input and output clock sources */
|
||||
clk_index[IN] = clk_map[IN][config->inclk];
|
||||
clk_index[OUT] = clk_map[OUT][config->outclk];
|
||||
clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
|
||||
clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
|
||||
|
||||
/* We only have output clock for ideal ratio mode */
|
||||
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
|
||||
@ -398,13 +437,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
|
||||
/* Set the channel number */
|
||||
channels = config->channel_num;
|
||||
|
||||
if (asrc_priv->channel_bits < 4)
|
||||
if (asrc_priv->soc->channel_bits < 4)
|
||||
channels /= 2;
|
||||
|
||||
/* Update channels for current pair */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
|
||||
ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
|
||||
ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));
|
||||
ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
|
||||
ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
|
||||
|
||||
/* Default setting: Automatic selection for processing mode */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
@ -531,7 +570,7 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
/* Odd channel number is not valid for older ASRC (channel_bits==3) */
|
||||
if (asrc_priv->channel_bits == 3)
|
||||
if (asrc_priv->soc->channel_bits == 3)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
|
||||
@ -905,6 +944,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int irq, ret, i;
|
||||
u32 map_idx;
|
||||
char tmp[16];
|
||||
|
||||
asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
|
||||
@ -964,14 +1004,37 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
asrc_priv->soc = of_device_get_match_data(&pdev->dev);
|
||||
if (!asrc_priv->soc) {
|
||||
dev_err(&pdev->dev, "failed to get soc data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
|
||||
asrc_priv->channel_bits = 3;
|
||||
clk_map[IN] = input_clk_map_imx35;
|
||||
clk_map[OUT] = output_clk_map_imx35;
|
||||
} else {
|
||||
asrc_priv->channel_bits = 4;
|
||||
clk_map[IN] = input_clk_map_imx53;
|
||||
clk_map[OUT] = output_clk_map_imx53;
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx35;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx53;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx53;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
|
||||
of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
|
||||
ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get clk map index\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (map_idx > 1) {
|
||||
dev_err(&pdev->dev, "unsupported clk map index\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
|
||||
asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
|
||||
asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
|
||||
} else {
|
||||
asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
|
||||
asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
|
||||
}
|
||||
}
|
||||
|
||||
ret = fsl_asrc_init(asrc_priv);
|
||||
@ -1113,9 +1176,31 @@ static const struct dev_pm_ops fsl_asrc_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
|
||||
};
|
||||
|
||||
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
|
||||
.use_edma = false,
|
||||
.channel_bits = 3,
|
||||
};
|
||||
|
||||
static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
|
||||
.use_edma = false,
|
||||
.channel_bits = 4,
|
||||
};
|
||||
|
||||
static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
|
||||
.use_edma = true,
|
||||
.channel_bits = 4,
|
||||
};
|
||||
|
||||
static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
|
||||
.use_edma = true,
|
||||
.channel_bits = 4,
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_asrc_ids[] = {
|
||||
{ .compatible = "fsl,imx35-asrc", },
|
||||
{ .compatible = "fsl,imx53-asrc", },
|
||||
{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
|
||||
{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
|
||||
{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
|
||||
{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
|
||||
|
@ -308,6 +308,29 @@ enum asrc_inclk {
|
||||
INCLK_SSI3_TX = 0x0b,
|
||||
INCLK_SPDIF_TX = 0x0c,
|
||||
INCLK_ASRCK1_CLK = 0x0f,
|
||||
|
||||
/* clocks for imx8 */
|
||||
INCLK_AUD_PLL_DIV_CLK0 = 0x10,
|
||||
INCLK_AUD_PLL_DIV_CLK1 = 0x11,
|
||||
INCLK_AUD_CLK0 = 0x12,
|
||||
INCLK_AUD_CLK1 = 0x13,
|
||||
INCLK_ESAI0_RX_CLK = 0x14,
|
||||
INCLK_ESAI0_TX_CLK = 0x15,
|
||||
INCLK_SPDIF0_RX = 0x16,
|
||||
INCLK_SPDIF1_RX = 0x17,
|
||||
INCLK_SAI0_RX_BCLK = 0x18,
|
||||
INCLK_SAI0_TX_BCLK = 0x19,
|
||||
INCLK_SAI1_RX_BCLK = 0x1a,
|
||||
INCLK_SAI1_TX_BCLK = 0x1b,
|
||||
INCLK_SAI2_RX_BCLK = 0x1c,
|
||||
INCLK_SAI3_RX_BCLK = 0x1d,
|
||||
INCLK_ASRC0_MUX_CLK = 0x1e,
|
||||
|
||||
INCLK_ESAI1_RX_CLK = 0x20,
|
||||
INCLK_ESAI1_TX_CLK = 0x21,
|
||||
INCLK_SAI6_TX_BCLK = 0x22,
|
||||
INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
|
||||
INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
|
||||
};
|
||||
|
||||
enum asrc_outclk {
|
||||
@ -325,9 +348,33 @@ enum asrc_outclk {
|
||||
OUTCLK_SSI3_RX = 0x0b,
|
||||
OUTCLK_SPDIF_RX = 0x0c,
|
||||
OUTCLK_ASRCK1_CLK = 0x0f,
|
||||
|
||||
/* clocks for imx8 */
|
||||
OUTCLK_AUD_PLL_DIV_CLK0 = 0x10,
|
||||
OUTCLK_AUD_PLL_DIV_CLK1 = 0x11,
|
||||
OUTCLK_AUD_CLK0 = 0x12,
|
||||
OUTCLK_AUD_CLK1 = 0x13,
|
||||
OUTCLK_ESAI0_RX_CLK = 0x14,
|
||||
OUTCLK_ESAI0_TX_CLK = 0x15,
|
||||
OUTCLK_SPDIF0_RX = 0x16,
|
||||
OUTCLK_SPDIF1_RX = 0x17,
|
||||
OUTCLK_SAI0_RX_BCLK = 0x18,
|
||||
OUTCLK_SAI0_TX_BCLK = 0x19,
|
||||
OUTCLK_SAI1_RX_BCLK = 0x1a,
|
||||
OUTCLK_SAI1_TX_BCLK = 0x1b,
|
||||
OUTCLK_SAI2_RX_BCLK = 0x1c,
|
||||
OUTCLK_SAI3_RX_BCLK = 0x1d,
|
||||
OUTCLK_ASRCO_MUX_CLK = 0x1e,
|
||||
|
||||
OUTCLK_ESAI1_RX_CLK = 0x20,
|
||||
OUTCLK_ESAI1_TX_CLK = 0x21,
|
||||
OUTCLK_SAI6_TX_BCLK = 0x22,
|
||||
OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
|
||||
OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
|
||||
};
|
||||
|
||||
#define ASRC_CLK_MAX_NUM 16
|
||||
#define ASRC_CLK_MAP_LEN 0x30
|
||||
|
||||
enum asrc_word_width {
|
||||
ASRC_WIDTH_24_BIT = 0,
|
||||
@ -387,6 +434,17 @@ struct dma_block {
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_asrc_soc_data: soc specific data
|
||||
*
|
||||
* @use_edma: using edma as dma device or not
|
||||
* @channel_bits: width of ASRCNCR register for each pair
|
||||
*/
|
||||
struct fsl_asrc_soc_data {
|
||||
bool use_edma;
|
||||
unsigned int channel_bits;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_asrc_pair: ASRC Pair private data
|
||||
*
|
||||
@ -431,8 +489,9 @@ struct fsl_asrc_pair {
|
||||
* @asrck_clk: clock sources to driver ASRC internal logic
|
||||
* @lock: spin lock for resource protection
|
||||
* @pair: pair pointers
|
||||
* @channel_bits: width of ASRCNCR register for each pair
|
||||
* @soc: soc specific data
|
||||
* @channel_avail: non-occupied channel numbers
|
||||
* @clk_map: clock map for input/output clock
|
||||
* @asrc_rate: default sample rate for ASoC Back-Ends
|
||||
* @asrc_width: default sample width for ASoC Back-Ends
|
||||
* @regcache_cfg: store register value of REG_ASRCFG
|
||||
@ -450,8 +509,9 @@ struct fsl_asrc {
|
||||
spinlock_t lock;
|
||||
|
||||
struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
|
||||
unsigned int channel_bits;
|
||||
const struct fsl_asrc_soc_data *soc;
|
||||
unsigned int channel_avail;
|
||||
unsigned char *clk_map[2];
|
||||
|
||||
int asrc_rate;
|
||||
int asrc_width;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user