Merge branch 'asoc-5.6' into asoc-next

This commit is contained in:
Mark Brown 2020-01-23 12:36:45 +00:00
commit a7196caf83
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
256 changed files with 23959 additions and 4063 deletions

View File

@ -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>;
};

View File

@ -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

View File

@ -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:

View 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";
};
...

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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

View 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>;
};
};
...

View 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>;
};
};
...

View 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>;
};

View File

@ -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:

View File

@ -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);

View File

@ -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,
&params_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;

View File

@ -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 */

View File

@ -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);
}

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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

View 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__ */

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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,

View File

@ -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

View 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);

View File

@ -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");

View File

@ -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;
}

View File

@ -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");

View File

@ -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"

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,
};
/*

View File

@ -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",

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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"},

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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]);

View File

@ -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, &gtm601_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 *)&gtm601_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 = {

View File

@ -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;

View File

@ -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 {

View File

@ -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),
{}
};

View File

@ -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
View 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");

View File

@ -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

View File

@ -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,

View File

@ -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[] = {

View File

@ -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,

View File

@ -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
View 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
View 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 */

View File

@ -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)

View File

@ -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
View 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
View 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__ */

View 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");

View 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__ */

View File

@ -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;
}

View File

@ -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;
}

View 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");

View 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

File diff suppressed because it is too large Load Diff

174
sound/soc/codecs/rt700.h Normal file
View 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__ */

View 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");

View 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

File diff suppressed because it is too large Load Diff

227
sound/soc/codecs/rt711.h Normal file
View 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__ */

View 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");

View 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
View 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
View 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__ */

View File

@ -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,

View File

@ -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",

View File

@ -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

File diff suppressed because it is too large Load Diff

1185
sound/soc/codecs/wsa881x.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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