Commit 39522868 authored by Mark Brown's avatar Mark Brown

ASoC: codecs: add support for WCD939x Codec

Merge series from Neil Armstrong <neil.armstrong@linaro.org>:

Add the main WCD9390/WCD9395 Audio Codec driver to support:
- 4 ADC inputs for up to 5 Analog Microphones
- 4 DMIC inputs for up to 8 Digital Microphones
- 4 Microphone BIAS
- Stereo Headphone output
- Mono EAR output
- MBHC engine for Headset Detection

This adds:
- bindings
- MBHC changes to support Type-C muc
- Soundwire Slave driver
- Code driver

The USB Mux subsystem support will be submitted separately
since it's a functionally separate subsystem connected over
I2C with it's own reset signal.
parents 5fe14c55 10f514bd
......@@ -15,6 +15,7 @@ description: |
allOf:
- $ref: dai-common.yaml#
- $ref: qcom,wcd93xx-common.yaml#
properties:
compatible:
......@@ -22,92 +23,12 @@ properties:
- qcom,wcd9380-codec
- qcom,wcd9385-codec
reset-gpios:
description: GPIO spec for reset line to use
maxItems: 1
us-euro-gpios:
description: GPIO spec for swapping gnd and mic segments
maxItems: 1
vdd-buck-supply:
description: A reference to the 1.8V buck supply
vdd-rxtx-supply:
description: A reference to the 1.8V rx supply
vdd-io-supply:
description: A reference to the 1.8V I/O supply
vdd-mic-bias-supply:
description: A reference to the 3.8V mic bias supply
qcom,tx-device:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A reference to Soundwire tx device phandle
qcom,rx-device:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A reference to Soundwire rx device phandle
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
qcom,hphl-jack-type-normally-closed:
description: Indicates that HPHL jack switch type is normally closed
type: boolean
qcom,ground-jack-type-normally-closed:
description: Indicates that Headset Ground switch type is normally closed
type: boolean
qcom,mbhc-headset-vthreshold-microvolt:
description: Voltage threshold value for headset detection
minimum: 0
maximum: 2850000
qcom,mbhc-headphone-vthreshold-microvolt:
description: Voltage threshold value for headphone detection
minimum: 0
maximum: 2850000
qcom,mbhc-buttons-vthreshold-microvolt:
description:
Array of 8 Voltage threshold values corresponding to headset
button0 - button7
minItems: 8
maxItems: 8
'#sound-dai-cells':
const: 1
required:
- compatible
- reset-gpios
- qcom,tx-device
- qcom,rx-device
- qcom,micbias1-microvolt
- qcom,micbias2-microvolt
- qcom,micbias3-microvolt
- qcom,micbias4-microvolt
- "#sound-dai-cells"
unevaluatedProperties: false
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/qcom,wcd939x-sdw.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SoundWire devices on WCD9390/WCD9395
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: |
Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC.
It has RX and TX Soundwire devices. This bindings is for the devices.
properties:
compatible:
const: sdw20217010e00
reg:
maxItems: 1
qcom,tx-port-mapping:
description: |
Specifies static port mapping between device and host tx ports.
In the order of the device port index.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 4
maxItems: 4
qcom,rx-port-mapping:
description: |
Specifies static port mapping between device and host rx ports.
In the order of device port index.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 6
maxItems: 6
required:
- compatible
- reg
additionalProperties: false
examples:
- |
soundwire@3210000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x03210000 0x2000>;
wcd938x_rx: codec@0,4 {
compatible = "sdw20217010e00";
reg = <0 4>;
qcom,rx-port-mapping = <1 2 3 4 5 6>;
};
};
soundwire@3230000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x03230000 0x2000>;
wcd938x_tx: codec@0,3 {
compatible = "sdw20217010e00";
reg = <0 3>;
qcom,tx-port-mapping = <2 3 4 5>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/qcom,wcd939x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm WCD9380/WCD9385 Audio Codec
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: |
Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC.
It has RX and TX Soundwire devices.
The WCD9390/WCD9395 IC has a functionally separate USB-C Mux subsystem
accessible over an I2C interface.
The Audio Headphone and Microphone data path between the Codec and the USB-C Mux
subsystems are external to the IC, thus requiring DT port-endpoint graph description
to handle USB-C altmode & orientation switching for Audio Accessory Mode.
allOf:
- $ref: dai-common.yaml#
- $ref: qcom,wcd93xx-common.yaml#
properties:
compatible:
oneOf:
- const: qcom,wcd9390-codec
- items:
- const: qcom,wcd9395-codec
- const: qcom,wcd9390-codec
mode-switch:
description: Flag the port as possible handler of altmode switching
type: boolean
orientation-switch:
description: Flag the port as possible handler of orientation switching
type: boolean
port:
$ref: /schemas/graph.yaml#/properties/port
description:
A port node to link the WCD939x Codec node to USB MUX subsystems for the
purpose of handling altmode muxing and orientation switching to detect and
enable Audio Accessory Mode.
required:
- compatible
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
codec {
compatible = "qcom,wcd9390-codec";
reset-gpios = <&tlmm 32 IRQ_TYPE_NONE>;
#sound-dai-cells = <1>;
qcom,tx-device = <&wcd939x_tx>;
qcom,rx-device = <&wcd939x_rx>;
qcom,micbias1-microvolt = <1800000>;
qcom,micbias2-microvolt = <1800000>;
qcom,micbias3-microvolt = <1800000>;
qcom,micbias4-microvolt = <1800000>;
qcom,hphl-jack-type-normally-closed;
qcom,ground-jack-type-normally-closed;
qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>;
qcom,mbhc-headphone-vthreshold-microvolt = <50000>;
};
/* ... */
soundwire@3210000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x03210000 0x2000>;
wcd939x_rx: codec@0,4 {
compatible = "sdw20217010e00";
reg = <0 4>;
qcom,rx-port-mapping = <1 2 3 4 5 6>;
};
};
soundwire@3230000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x03230000 0x2000>;
wcd938x_tx: codec@0,3 {
compatible = "sdw20217010e00";
reg = <0 3>;
qcom,tx-port-mapping = <2 3 4 5>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/qcom,wcd93xx-common.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Common properties for Qualcomm WCD93xx Audio Codec
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
properties:
reset-gpios:
description: GPIO spec for reset line to use
maxItems: 1
vdd-buck-supply:
description: A reference to the 1.8V buck supply
vdd-rxtx-supply:
description: A reference to the 1.8V rx supply
vdd-io-supply:
description: A reference to the 1.8V I/O supply
vdd-mic-bias-supply:
description: A reference to the 3.8V mic bias supply
qcom,tx-device:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A reference to Soundwire tx device phandle
qcom,rx-device:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A reference to Soundwire rx device phandle
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
qcom,hphl-jack-type-normally-closed:
description: Indicates that HPHL jack switch type is normally closed
type: boolean
qcom,ground-jack-type-normally-closed:
description: Indicates that Headset Ground switch type is normally closed
type: boolean
qcom,mbhc-headset-vthreshold-microvolt:
description: Voltage threshold value for headset detection
minimum: 0
maximum: 2850000
qcom,mbhc-headphone-vthreshold-microvolt:
description: Voltage threshold value for headphone detection
minimum: 0
maximum: 2850000
qcom,mbhc-buttons-vthreshold-microvolt:
description:
Array of 8 Voltage threshold values corresponding to headset
button0 - button7
minItems: 8
maxItems: 8
'#sound-dai-cells':
const: 1
required:
- reset-gpios
- qcom,tx-device
- qcom,rx-device
- qcom,micbias1-microvolt
- qcom,micbias2-microvolt
- qcom,micbias3-microvolt
- qcom,micbias4-microvolt
- "#sound-dai-cells"
additionalProperties: true
......@@ -276,6 +276,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_WCD9335
imply SND_SOC_WCD934X
imply SND_SOC_WCD938X_SDW
imply SND_SOC_WCD939X_SDW
imply SND_SOC_LPASS_MACRO_COMMON
imply SND_SOC_LPASS_RX_MACRO
imply SND_SOC_LPASS_TX_MACRO
......@@ -2059,6 +2060,24 @@ config SND_SOC_WCD938X_SDW
The WCD9380/9385 is a audio codec IC Integrated in
Qualcomm SoCs like SM8250.
config SND_SOC_WCD939X
depends on SND_SOC_WCD939X_SDW
tristate
depends on SOUNDWIRE || !SOUNDWIRE
depends on TYPEC || !TYPEC
select SND_SOC_WCD_CLASSH
config SND_SOC_WCD939X_SDW
tristate "WCD9390/WCD9395 Codec - SDW"
select SND_SOC_WCD939X
select SND_SOC_WCD_MBHC
select REGMAP_IRQ
depends on SOUNDWIRE
select REGMAP_SOUNDWIRE
help
The WCD9390/9395 is a audio codec IC Integrated in
Qualcomm SoCs like SM8650.
config SND_SOC_WL1273
tristate
......
......@@ -313,6 +313,8 @@ snd-soc-wcd9335-objs := wcd9335.o
snd-soc-wcd934x-objs := wcd934x.o
snd-soc-wcd938x-objs := wcd938x.o
snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o
snd-soc-wcd939x-objs := wcd939x.o
snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
......@@ -703,6 +705,11 @@ ifdef CONFIG_SND_SOC_WCD938X_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o
endif
obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x.o
ifdef CONFIG_SND_SOC_WCD939X_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
endif
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
......
......@@ -47,6 +47,7 @@ enum wcd_codec_version {
/* New CLSH after this */
WCD937X = 2,
WCD938X = 3,
WCD939X = 4,
};
struct wcd_clsh_ctrl;
......
......@@ -16,6 +16,7 @@
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
#define GND_MIC_SWAP_THRESHOLD 4
#define GND_MIC_USBC_SWAP_THRESHOLD 2
#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
#define HPHL_CROSS_CONN_THRESHOLD 100
#define HS_VREF_MIN_VAL 1400
......@@ -52,12 +53,15 @@ struct wcd_mbhc {
struct wcd_mbhc_field *fields;
/* Delayed work to report long button press */
struct delayed_work mbhc_btn_dwork;
/* Work to handle plug report */
struct work_struct mbhc_plug_detect_work;
/* Work to correct accessory type */
struct work_struct correct_plug_swch;
struct mutex lock;
int buttons_pressed;
u32 hph_status; /* track headhpone status */
u8 current_plug;
unsigned int swap_thr;
bool is_btn_press;
bool in_swch_irq_handler;
bool hs_detect_work_stop;
......@@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
}
}
static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
static void mbhc_plug_detect_fn(struct work_struct *work)
{
struct snd_soc_component *component;
struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work);
struct snd_soc_component *component = mbhc->component;
enum snd_jack_types jack_type;
struct wcd_mbhc *mbhc = data;
bool detection_type;
component = mbhc->component;
mutex_lock(&mbhc->lock);
mbhc->in_swch_irq_handler = true;
......@@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
exit:
mbhc->in_swch_irq_handler = false;
mutex_unlock(&mbhc->lock);
}
static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
{
struct wcd_mbhc *mbhc = data;
if (!mbhc->cfg->typec_analog_mux)
schedule_work(&mbhc->mbhc_plug_detect_work);
return IRQ_HANDLED;
}
int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc)
{
if (!mbhc || !mbhc->cfg->typec_analog_mux)
return -EINVAL;
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(mbhc->component, false);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0);
schedule_work(&mbhc->mbhc_plug_detect_work);
return 0;
}
EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);
int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc)
{
if (!mbhc || !mbhc->cfg->typec_analog_mux)
return -EINVAL;
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(mbhc->component, true);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
schedule_work(&mbhc->mbhc_plug_detect_work);
return 0;
}
EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);
static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
{
int mask = 0;
......@@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mutex_lock(&mbhc->lock);
/* enable HS detection */
if (mbhc->cfg->typec_analog_mux)
mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
else
mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD;
/* setup HS detection */
if (mbhc->mbhc_cb->hph_pull_up_control_v2)
mbhc->mbhc_cb->hph_pull_up_control_v2(component,
HS_PULLUP_I_DEFAULT);
mbhc->cfg->typec_analog_mux ?
HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT);
else if (mbhc->mbhc_cb->hph_pull_up_control)
mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
mbhc->mbhc_cb->hph_pull_up_control(component,
mbhc->cfg->typec_analog_mux ?
I_OFF : I_DEFAULT);
else
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
mbhc->cfg->typec_analog_mux ? 0 : 3);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
......@@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
/* Plug detect is triggered manually if analog goes through USBCC */
if (mbhc->cfg->typec_analog_mux)
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
else
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
/* Insertion debounce set to 96ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
if (mbhc->cfg->typec_analog_mux)
/* Insertion debounce set to 48ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4);
else
/* Insertion debounce set to 96ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
/* Button Debounce set to 16ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
......@@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->mbhc_bias(component, true);
/* enable MBHC clock */
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(component, true);
mbhc->mbhc_cb->clk_setup(component,
mbhc->cfg->typec_analog_mux ? false : true);
/* program HS_VREF value */
wcd_program_hs_vref(mbhc);
......@@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
do {
cross_conn = wcd_check_cross_conn(mbhc);
try++;
} while (try < GND_MIC_SWAP_THRESHOLD);
} while (try < mbhc->swap_thr);
if (cross_conn > 0) {
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
......@@ -1183,7 +1246,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
cross_conn = wcd_check_cross_conn(mbhc);
if (cross_conn > 0) { /* cross-connection */
pt_gnd_mic_swap_cnt++;
if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
if (pt_gnd_mic_swap_cnt < mbhc->swap_thr)
continue;
else
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
......@@ -1194,7 +1257,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
} else /* Error if (cross_conn < 0) */
continue;
if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
/* US_EU gpio present, flip switch */
if (mbhc->cfg->swap_gnd_mic) {
if (mbhc->cfg->swap_gnd_mic(component, true))
......@@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
mutex_init(&mbhc->lock);
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn);
ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
wcd_mbhc_mech_plug_detect_irq,
......@@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
mutex_lock(&mbhc->lock);
wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
cancel_work_sync(&mbhc->mbhc_plug_detect_work);
mutex_unlock(&mbhc->lock);
kfree(mbhc);
......
......@@ -193,6 +193,7 @@ struct wcd_mbhc_config {
int v_hs_max;
int num_btn;
bool mono_stero_detection;
bool typec_analog_mux;
bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active);
bool hs_ext_micbias;
bool gnd_det_en;
......@@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg,
void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type);
int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc);
int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc);
int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc);
struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
const struct wcd_mbhc_cb *mbhc_cb,
const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment