162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * wm_hubs.c  --  WM8993/4 common code
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2009-12 Wolfson Microelectronics plc
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/moduleparam.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/pm.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/mfd/wm8994/registers.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/pcm.h>
1962306a36Sopenharmony_ci#include <sound/pcm_params.h>
2062306a36Sopenharmony_ci#include <sound/soc.h>
2162306a36Sopenharmony_ci#include <sound/initval.h>
2262306a36Sopenharmony_ci#include <sound/tlv.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "wm8993.h"
2562306a36Sopenharmony_ci#include "wm_hubs.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciconst DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0);
2862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
3162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
3262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
3362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
3462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
3562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
3662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
3762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(spkboost_tlv,
3862306a36Sopenharmony_ci	0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
3962306a36Sopenharmony_ci	7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0)
4062306a36Sopenharmony_ci);
4162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic const char *speaker_ref_text[] = {
4462306a36Sopenharmony_ci	"SPKVDD/2",
4562306a36Sopenharmony_ci	"VMID",
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(speaker_ref,
4962306a36Sopenharmony_ci			    WM8993_SPEAKER_MIXER, 8, speaker_ref_text);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic const char *speaker_mode_text[] = {
5262306a36Sopenharmony_ci	"Class D",
5362306a36Sopenharmony_ci	"Class AB",
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(speaker_mode,
5762306a36Sopenharmony_ci			    WM8993_SPKMIXR_ATTENUATION, 8, speaker_mode_text);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void wait_for_dc_servo(struct snd_soc_component *component, unsigned int op)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
6262306a36Sopenharmony_ci	unsigned int reg;
6362306a36Sopenharmony_ci	int count = 0;
6462306a36Sopenharmony_ci	int timeout;
6562306a36Sopenharmony_ci	unsigned int val;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* Trigger the command */
7062306a36Sopenharmony_ci	snd_soc_component_write(component, WM8993_DC_SERVO_0, val);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	dev_dbg(component->dev, "Waiting for DC servo...\n");
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (hubs->dcs_done_irq)
7562306a36Sopenharmony_ci		timeout = 4;
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		timeout = 400;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	do {
8062306a36Sopenharmony_ci		count++;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		if (hubs->dcs_done_irq)
8362306a36Sopenharmony_ci			wait_for_completion_timeout(&hubs->dcs_done,
8462306a36Sopenharmony_ci						    msecs_to_jiffies(250));
8562306a36Sopenharmony_ci		else
8662306a36Sopenharmony_ci			msleep(1);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		reg = snd_soc_component_read(component, WM8993_DC_SERVO_0);
8962306a36Sopenharmony_ci		dev_dbg(component->dev, "DC servo: %x\n", reg);
9062306a36Sopenharmony_ci	} while (reg & op && count < timeout);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (reg & op)
9362306a36Sopenharmony_ci		dev_err(component->dev, "Timed out waiting for DC Servo %x\n",
9462306a36Sopenharmony_ci			op);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciirqreturn_t wm_hubs_dcs_done(int irq, void *data)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct wm_hubs_data *hubs = data;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	complete(&hubs->dcs_done);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return IRQ_HANDLED;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic bool wm_hubs_dac_hp_direct(struct snd_soc_component *component)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int reg;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* If we're going via the mixer we'll need to do additional checks */
11262306a36Sopenharmony_ci	reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER1);
11362306a36Sopenharmony_ci	if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
11462306a36Sopenharmony_ci		if (reg & ~WM8993_DACL_TO_MIXOUTL) {
11562306a36Sopenharmony_ci			dev_vdbg(component->dev, "Analogue paths connected: %x\n",
11662306a36Sopenharmony_ci				 reg & ~WM8993_DACL_TO_HPOUT1L);
11762306a36Sopenharmony_ci			return false;
11862306a36Sopenharmony_ci		} else {
11962306a36Sopenharmony_ci			dev_vdbg(component->dev, "HPL connected to mixer\n");
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		dev_vdbg(component->dev, "HPL connected to DAC\n");
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER2);
12662306a36Sopenharmony_ci	if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
12762306a36Sopenharmony_ci		if (reg & ~WM8993_DACR_TO_MIXOUTR) {
12862306a36Sopenharmony_ci			dev_vdbg(component->dev, "Analogue paths connected: %x\n",
12962306a36Sopenharmony_ci				 reg & ~WM8993_DACR_TO_HPOUT1R);
13062306a36Sopenharmony_ci			return false;
13162306a36Sopenharmony_ci		} else {
13262306a36Sopenharmony_ci			dev_vdbg(component->dev, "HPR connected to mixer\n");
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		dev_vdbg(component->dev, "HPR connected to DAC\n");
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return true;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistruct wm_hubs_dcs_cache {
14262306a36Sopenharmony_ci	struct list_head list;
14362306a36Sopenharmony_ci	unsigned int left;
14462306a36Sopenharmony_ci	unsigned int right;
14562306a36Sopenharmony_ci	u16 dcs_cfg;
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic bool wm_hubs_dcs_cache_get(struct snd_soc_component *component,
14962306a36Sopenharmony_ci				  struct wm_hubs_dcs_cache **entry)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
15262306a36Sopenharmony_ci	struct wm_hubs_dcs_cache *cache;
15362306a36Sopenharmony_ci	unsigned int left, right;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME);
15662306a36Sopenharmony_ci	left &= WM8993_HPOUT1L_VOL_MASK;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME);
15962306a36Sopenharmony_ci	right &= WM8993_HPOUT1R_VOL_MASK;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	list_for_each_entry(cache, &hubs->dcs_cache, list) {
16262306a36Sopenharmony_ci		if (cache->left != left || cache->right != right)
16362306a36Sopenharmony_ci			continue;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		*entry = cache;
16662306a36Sopenharmony_ci		return true;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return false;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void wm_hubs_dcs_cache_set(struct snd_soc_component *component, u16 dcs_cfg)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
17562306a36Sopenharmony_ci	struct wm_hubs_dcs_cache *cache;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (hubs->no_cache_dac_hp_direct)
17862306a36Sopenharmony_ci		return;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	cache = devm_kzalloc(component->dev, sizeof(*cache), GFP_KERNEL);
18162306a36Sopenharmony_ci	if (!cache)
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	cache->left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME);
18562306a36Sopenharmony_ci	cache->left &= WM8993_HPOUT1L_VOL_MASK;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	cache->right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME);
18862306a36Sopenharmony_ci	cache->right &= WM8993_HPOUT1R_VOL_MASK;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	cache->dcs_cfg = dcs_cfg;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	list_add_tail(&cache->list, &hubs->dcs_cache);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int wm_hubs_read_dc_servo(struct snd_soc_component *component,
19662306a36Sopenharmony_ci				  u16 *reg_l, u16 *reg_r)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
19962306a36Sopenharmony_ci	u16 dcs_reg, reg;
20062306a36Sopenharmony_ci	int ret = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	switch (hubs->dcs_readback_mode) {
20362306a36Sopenharmony_ci	case 2:
20462306a36Sopenharmony_ci		dcs_reg = WM8994_DC_SERVO_4E;
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case 1:
20762306a36Sopenharmony_ci		dcs_reg = WM8994_DC_SERVO_READBACK;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	default:
21062306a36Sopenharmony_ci		dcs_reg = WM8993_DC_SERVO_3;
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Different chips in the family support different readback
21562306a36Sopenharmony_ci	 * methods.
21662306a36Sopenharmony_ci	 */
21762306a36Sopenharmony_ci	switch (hubs->dcs_readback_mode) {
21862306a36Sopenharmony_ci	case 0:
21962306a36Sopenharmony_ci		*reg_l = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_1)
22062306a36Sopenharmony_ci			& WM8993_DCS_INTEG_CHAN_0_MASK;
22162306a36Sopenharmony_ci		*reg_r = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_2)
22262306a36Sopenharmony_ci			& WM8993_DCS_INTEG_CHAN_1_MASK;
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case 2:
22562306a36Sopenharmony_ci	case 1:
22662306a36Sopenharmony_ci		reg = snd_soc_component_read(component, dcs_reg);
22762306a36Sopenharmony_ci		*reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
22862306a36Sopenharmony_ci			>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
22962306a36Sopenharmony_ci		*reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	default:
23262306a36Sopenharmony_ci		WARN(1, "Unknown DCS readback method\n");
23362306a36Sopenharmony_ci		ret = -1;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	return ret;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/*
23962306a36Sopenharmony_ci * Startup calibration of the DC servo
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_cistatic void enable_dc_servo(struct snd_soc_component *component)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
24462306a36Sopenharmony_ci	struct wm_hubs_dcs_cache *cache;
24562306a36Sopenharmony_ci	s8 offset;
24662306a36Sopenharmony_ci	u16 reg_l, reg_r, dcs_cfg, dcs_reg;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	switch (hubs->dcs_readback_mode) {
24962306a36Sopenharmony_ci	case 2:
25062306a36Sopenharmony_ci		dcs_reg = WM8994_DC_SERVO_4E;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	default:
25362306a36Sopenharmony_ci		dcs_reg = WM8993_DC_SERVO_3;
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* If we're using a digital only path and have a previously
25862306a36Sopenharmony_ci	 * callibrated DC servo offset stored then use that. */
25962306a36Sopenharmony_ci	if (wm_hubs_dac_hp_direct(component) &&
26062306a36Sopenharmony_ci	    wm_hubs_dcs_cache_get(component, &cache)) {
26162306a36Sopenharmony_ci		dev_dbg(component->dev, "Using cached DCS offset %x for %d,%d\n",
26262306a36Sopenharmony_ci			cache->dcs_cfg, cache->left, cache->right);
26362306a36Sopenharmony_ci		snd_soc_component_write(component, dcs_reg, cache->dcs_cfg);
26462306a36Sopenharmony_ci		wait_for_dc_servo(component,
26562306a36Sopenharmony_ci				  WM8993_DCS_TRIG_DAC_WR_0 |
26662306a36Sopenharmony_ci				  WM8993_DCS_TRIG_DAC_WR_1);
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (hubs->series_startup) {
27162306a36Sopenharmony_ci		/* Set for 32 series updates */
27262306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_DC_SERVO_1,
27362306a36Sopenharmony_ci				    WM8993_DCS_SERIES_NO_01_MASK,
27462306a36Sopenharmony_ci				    32 << WM8993_DCS_SERIES_NO_01_SHIFT);
27562306a36Sopenharmony_ci		wait_for_dc_servo(component,
27662306a36Sopenharmony_ci				  WM8993_DCS_TRIG_SERIES_0 |
27762306a36Sopenharmony_ci				  WM8993_DCS_TRIG_SERIES_1);
27862306a36Sopenharmony_ci	} else {
27962306a36Sopenharmony_ci		wait_for_dc_servo(component,
28062306a36Sopenharmony_ci				  WM8993_DCS_TRIG_STARTUP_0 |
28162306a36Sopenharmony_ci				  WM8993_DCS_TRIG_STARTUP_1);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (wm_hubs_read_dc_servo(component, &reg_l, &reg_r) < 0)
28562306a36Sopenharmony_ci		return;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	dev_dbg(component->dev, "DCS input: %x %x\n", reg_l, reg_r);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Apply correction to DC servo result */
29062306a36Sopenharmony_ci	if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
29162306a36Sopenharmony_ci		dev_dbg(component->dev,
29262306a36Sopenharmony_ci			"Applying %d/%d code DC servo correction\n",
29362306a36Sopenharmony_ci			hubs->dcs_codes_l, hubs->dcs_codes_r);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* HPOUT1R */
29662306a36Sopenharmony_ci		offset = (s8)reg_r;
29762306a36Sopenharmony_ci		dev_dbg(component->dev, "DCS right %d->%d\n", offset,
29862306a36Sopenharmony_ci			offset + hubs->dcs_codes_r);
29962306a36Sopenharmony_ci		offset += hubs->dcs_codes_r;
30062306a36Sopenharmony_ci		dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		/* HPOUT1L */
30362306a36Sopenharmony_ci		offset = (s8)reg_l;
30462306a36Sopenharmony_ci		dev_dbg(component->dev, "DCS left %d->%d\n", offset,
30562306a36Sopenharmony_ci			offset + hubs->dcs_codes_l);
30662306a36Sopenharmony_ci		offset += hubs->dcs_codes_l;
30762306a36Sopenharmony_ci		dcs_cfg |= (u8)offset;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		dev_dbg(component->dev, "DCS result: %x\n", dcs_cfg);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		/* Do it */
31262306a36Sopenharmony_ci		snd_soc_component_write(component, dcs_reg, dcs_cfg);
31362306a36Sopenharmony_ci		wait_for_dc_servo(component,
31462306a36Sopenharmony_ci				  WM8993_DCS_TRIG_DAC_WR_0 |
31562306a36Sopenharmony_ci				  WM8993_DCS_TRIG_DAC_WR_1);
31662306a36Sopenharmony_ci	} else {
31762306a36Sopenharmony_ci		dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
31862306a36Sopenharmony_ci		dcs_cfg |= reg_l;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Save the callibrated offset if we're in class W mode and
32262306a36Sopenharmony_ci	 * therefore don't have any analogue signal mixed in. */
32362306a36Sopenharmony_ci	if (wm_hubs_dac_hp_direct(component))
32462306a36Sopenharmony_ci		wm_hubs_dcs_cache_set(component, dcs_cfg);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/*
32862306a36Sopenharmony_ci * Update the DC servo calibration on gain changes
32962306a36Sopenharmony_ci */
33062306a36Sopenharmony_cistatic int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
33162306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
33462306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = snd_soc_put_volsw(kcontrol, ucontrol);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* If we're applying an offset correction then updating the
34062306a36Sopenharmony_ci	 * callibration would be likely to introduce further offsets. */
34162306a36Sopenharmony_ci	if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
34262306a36Sopenharmony_ci		return ret;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* Only need to do this if the outputs are active */
34562306a36Sopenharmony_ci	if (snd_soc_component_read(component, WM8993_POWER_MANAGEMENT_1)
34662306a36Sopenharmony_ci	    & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
34762306a36Sopenharmony_ci		snd_soc_component_update_bits(component,
34862306a36Sopenharmony_ci				    WM8993_DC_SERVO_0,
34962306a36Sopenharmony_ci				    WM8993_DCS_TRIG_SINGLE_0 |
35062306a36Sopenharmony_ci				    WM8993_DCS_TRIG_SINGLE_1,
35162306a36Sopenharmony_ci				    WM8993_DCS_TRIG_SINGLE_0 |
35262306a36Sopenharmony_ci				    WM8993_DCS_TRIG_SINGLE_1);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return ret;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const struct snd_kcontrol_new analogue_snd_controls[] = {
35862306a36Sopenharmony_ciSOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
35962306a36Sopenharmony_ci	       inpga_tlv),
36062306a36Sopenharmony_ciSOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
36162306a36Sopenharmony_ciSOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 6, 1, 0),
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ciSOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
36462306a36Sopenharmony_ci	       inpga_tlv),
36562306a36Sopenharmony_ciSOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
36662306a36Sopenharmony_ciSOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 6, 1, 0),
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciSOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
37062306a36Sopenharmony_ci	       inpga_tlv),
37162306a36Sopenharmony_ciSOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
37262306a36Sopenharmony_ciSOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 6, 1, 0),
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ciSOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
37562306a36Sopenharmony_ci	       inpga_tlv),
37662306a36Sopenharmony_ciSOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
37762306a36Sopenharmony_ciSOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 6, 1, 0),
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0,
38062306a36Sopenharmony_ci	       inmix_sw_tlv),
38162306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0,
38262306a36Sopenharmony_ci	       inmix_sw_tlv),
38362306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0,
38462306a36Sopenharmony_ci	       inmix_tlv),
38562306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv),
38662306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0,
38762306a36Sopenharmony_ci	       inmix_tlv),
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0,
39062306a36Sopenharmony_ci	       inmix_sw_tlv),
39162306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0,
39262306a36Sopenharmony_ci	       inmix_sw_tlv),
39362306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0,
39462306a36Sopenharmony_ci	       inmix_tlv),
39562306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv),
39662306a36Sopenharmony_ciSOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0,
39762306a36Sopenharmony_ci	       inmix_tlv),
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1,
40062306a36Sopenharmony_ci	       outmix_tlv),
40162306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1,
40262306a36Sopenharmony_ci	       outmix_tlv),
40362306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1,
40462306a36Sopenharmony_ci	       outmix_tlv),
40562306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1,
40662306a36Sopenharmony_ci	       outmix_tlv),
40762306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1,
40862306a36Sopenharmony_ci	       outmix_tlv),
40962306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer Right Input Volume",
41062306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv),
41162306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer Left Input Volume",
41262306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv),
41362306a36Sopenharmony_ciSOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1,
41462306a36Sopenharmony_ci	       outmix_tlv),
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer IN2LN Volume",
41762306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
41862306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer IN2RN Volume",
41962306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv),
42062306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer IN1L Volume",
42162306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv),
42262306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer IN1R Volume",
42362306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv),
42462306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer IN2RP Volume",
42562306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv),
42662306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer Left Input Volume",
42762306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv),
42862306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer Right Input Volume",
42962306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
43062306a36Sopenharmony_ciSOC_SINGLE_TLV("Right Output Mixer DAC Volume",
43162306a36Sopenharmony_ci	       WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv),
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME,
43462306a36Sopenharmony_ci		 WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv),
43562306a36Sopenharmony_ciSOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME,
43662306a36Sopenharmony_ci	     WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0),
43762306a36Sopenharmony_ciSOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME,
43862306a36Sopenharmony_ci	     WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0),
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciSOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1),
44162306a36Sopenharmony_ciSOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv),
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION,
44462306a36Sopenharmony_ci	       5, 1, 1, wm_hubs_spkmix_tlv),
44562306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION,
44662306a36Sopenharmony_ci	       4, 1, 1, wm_hubs_spkmix_tlv),
44762306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION,
44862306a36Sopenharmony_ci	       3, 1, 1, wm_hubs_spkmix_tlv),
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION,
45162306a36Sopenharmony_ci	       5, 1, 1, wm_hubs_spkmix_tlv),
45262306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION,
45362306a36Sopenharmony_ci	       4, 1, 1, wm_hubs_spkmix_tlv),
45462306a36Sopenharmony_ciSOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION,
45562306a36Sopenharmony_ci	       3, 1, 1, wm_hubs_spkmix_tlv),
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Speaker Mixer Volume",
45862306a36Sopenharmony_ci		 WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION,
45962306a36Sopenharmony_ci		 0, 3, 1, spkmixout_tlv),
46062306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Speaker Volume",
46162306a36Sopenharmony_ci		 WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
46262306a36Sopenharmony_ci		 0, 63, 0, outpga_tlv),
46362306a36Sopenharmony_ciSOC_DOUBLE_R("Speaker Switch",
46462306a36Sopenharmony_ci	     WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
46562306a36Sopenharmony_ci	     6, 1, 0),
46662306a36Sopenharmony_ciSOC_DOUBLE_R("Speaker ZC Switch",
46762306a36Sopenharmony_ci	     WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
46862306a36Sopenharmony_ci	     7, 1, 0),
46962306a36Sopenharmony_ciSOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 3, 0, 7, 0,
47062306a36Sopenharmony_ci	       spkboost_tlv),
47162306a36Sopenharmony_ciSOC_ENUM("Speaker Reference", speaker_ref),
47262306a36Sopenharmony_ciSOC_ENUM("Speaker Mode", speaker_mode),
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ciSOC_DOUBLE_R_EXT_TLV("Headphone Volume",
47562306a36Sopenharmony_ci		     WM8993_LEFT_OUTPUT_VOLUME, WM8993_RIGHT_OUTPUT_VOLUME,
47662306a36Sopenharmony_ci		     0, 63, 0, snd_soc_get_volsw, wm8993_put_dc_servo,
47762306a36Sopenharmony_ci		     outpga_tlv),
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ciSOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME,
48062306a36Sopenharmony_ci	     WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0),
48162306a36Sopenharmony_ciSOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME,
48262306a36Sopenharmony_ci	     WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ciSOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1),
48562306a36Sopenharmony_ciSOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1),
48662306a36Sopenharmony_ciSOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1,
48762306a36Sopenharmony_ci	       line_tlv),
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciSOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1),
49062306a36Sopenharmony_ciSOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1),
49162306a36Sopenharmony_ciSOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
49262306a36Sopenharmony_ci	       line_tlv),
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int hp_supply_event(struct snd_soc_dapm_widget *w,
49662306a36Sopenharmony_ci			   struct snd_kcontrol *kcontrol, int event)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
49962306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	switch (event) {
50262306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
50362306a36Sopenharmony_ci		switch (hubs->hp_startup_mode) {
50462306a36Sopenharmony_ci		case 0:
50562306a36Sopenharmony_ci			break;
50662306a36Sopenharmony_ci		case 1:
50762306a36Sopenharmony_ci			/* Enable the headphone amp */
50862306a36Sopenharmony_ci			snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
50962306a36Sopenharmony_ci					    WM8993_HPOUT1L_ENA |
51062306a36Sopenharmony_ci					    WM8993_HPOUT1R_ENA,
51162306a36Sopenharmony_ci					    WM8993_HPOUT1L_ENA |
51262306a36Sopenharmony_ci					    WM8993_HPOUT1R_ENA);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci			/* Enable the second stage */
51562306a36Sopenharmony_ci			snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
51662306a36Sopenharmony_ci					    WM8993_HPOUT1L_DLY |
51762306a36Sopenharmony_ci					    WM8993_HPOUT1R_DLY,
51862306a36Sopenharmony_ci					    WM8993_HPOUT1L_DLY |
51962306a36Sopenharmony_ci					    WM8993_HPOUT1R_DLY);
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		default:
52262306a36Sopenharmony_ci			dev_err(component->dev, "Unknown HP startup mode %d\n",
52362306a36Sopenharmony_ci				hubs->hp_startup_mode);
52462306a36Sopenharmony_ci			break;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMD:
52962306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1,
53062306a36Sopenharmony_ci				    WM8993_CP_ENA, 0);
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return 0;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int hp_event(struct snd_soc_dapm_widget *w,
53862306a36Sopenharmony_ci		    struct snd_kcontrol *kcontrol, int event)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
54162306a36Sopenharmony_ci	unsigned int reg = snd_soc_component_read(component, WM8993_ANALOGUE_HP_0);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	switch (event) {
54462306a36Sopenharmony_ci	case SND_SOC_DAPM_POST_PMU:
54562306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1,
54662306a36Sopenharmony_ci				    WM8993_CP_ENA, WM8993_CP_ENA);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		msleep(5);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
55162306a36Sopenharmony_ci				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
55262306a36Sopenharmony_ci				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
55562306a36Sopenharmony_ci		snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_DC_SERVO_1,
55862306a36Sopenharmony_ci				    WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		enable_dc_servo(component);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
56362306a36Sopenharmony_ci			WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
56462306a36Sopenharmony_ci		snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg);
56562306a36Sopenharmony_ci		break;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMD:
56862306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
56962306a36Sopenharmony_ci				    WM8993_HPOUT1L_OUTP |
57062306a36Sopenharmony_ci				    WM8993_HPOUT1R_OUTP |
57162306a36Sopenharmony_ci				    WM8993_HPOUT1L_RMV_SHORT |
57262306a36Sopenharmony_ci				    WM8993_HPOUT1R_RMV_SHORT, 0);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
57562306a36Sopenharmony_ci				    WM8993_HPOUT1L_DLY |
57662306a36Sopenharmony_ci				    WM8993_HPOUT1R_DLY, 0);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		snd_soc_component_write(component, WM8993_DC_SERVO_0, 0);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
58162306a36Sopenharmony_ci				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
58262306a36Sopenharmony_ci				    0);
58362306a36Sopenharmony_ci		break;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int earpiece_event(struct snd_soc_dapm_widget *w,
59062306a36Sopenharmony_ci			  struct snd_kcontrol *control, int event)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
59362306a36Sopenharmony_ci	u16 reg = snd_soc_component_read(component, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	switch (event) {
59662306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
59762306a36Sopenharmony_ci		reg |= WM8993_HPOUT2_IN_ENA;
59862306a36Sopenharmony_ci		snd_soc_component_write(component, WM8993_ANTIPOP1, reg);
59962306a36Sopenharmony_ci		udelay(50);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case SND_SOC_DAPM_POST_PMD:
60362306a36Sopenharmony_ci		snd_soc_component_write(component, WM8993_ANTIPOP1, reg);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	default:
60762306a36Sopenharmony_ci		WARN(1, "Invalid event %d\n", event);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return 0;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int lineout_event(struct snd_soc_dapm_widget *w,
61562306a36Sopenharmony_ci			 struct snd_kcontrol *control, int event)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
61862306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
61962306a36Sopenharmony_ci	bool *flag;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	switch (w->shift) {
62262306a36Sopenharmony_ci	case WM8993_LINEOUT1N_ENA_SHIFT:
62362306a36Sopenharmony_ci		flag = &hubs->lineout1n_ena;
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	case WM8993_LINEOUT1P_ENA_SHIFT:
62662306a36Sopenharmony_ci		flag = &hubs->lineout1p_ena;
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	case WM8993_LINEOUT2N_ENA_SHIFT:
62962306a36Sopenharmony_ci		flag = &hubs->lineout2n_ena;
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci	case WM8993_LINEOUT2P_ENA_SHIFT:
63262306a36Sopenharmony_ci		flag = &hubs->lineout2p_ena;
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	default:
63562306a36Sopenharmony_ci		WARN(1, "Unknown line output");
63662306a36Sopenharmony_ci		return -EINVAL;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	*flag = SND_SOC_DAPM_EVENT_ON(event);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int micbias_event(struct snd_soc_dapm_widget *w,
64562306a36Sopenharmony_ci			 struct snd_kcontrol *kcontrol, int event)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
64862306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	switch (w->shift) {
65162306a36Sopenharmony_ci	case WM8993_MICB1_ENA_SHIFT:
65262306a36Sopenharmony_ci		if (hubs->micb1_delay)
65362306a36Sopenharmony_ci			msleep(hubs->micb1_delay);
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci	case WM8993_MICB2_ENA_SHIFT:
65662306a36Sopenharmony_ci		if (hubs->micb2_delay)
65762306a36Sopenharmony_ci			msleep(hubs->micb2_delay);
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	default:
66062306a36Sopenharmony_ci		return -EINVAL;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return 0;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_civoid wm_hubs_update_class_w(struct snd_soc_component *component)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
66962306a36Sopenharmony_ci	int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (!wm_hubs_dac_hp_direct(component))
67262306a36Sopenharmony_ci		enable = false;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (hubs->check_class_w_digital && !hubs->check_class_w_digital(component))
67562306a36Sopenharmony_ci		enable = false;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	dev_vdbg(component->dev, "Class W %s\n", enable ? "enabled" : "disabled");
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_CLASS_W_0,
68062306a36Sopenharmony_ci			    WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	snd_soc_component_write(component, WM8993_LEFT_OUTPUT_VOLUME,
68362306a36Sopenharmony_ci		      snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME));
68462306a36Sopenharmony_ci	snd_soc_component_write(component, WM8993_RIGHT_OUTPUT_VOLUME,
68562306a36Sopenharmony_ci		      snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME));
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
69062306a36Sopenharmony_ci	SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
69162306a36Sopenharmony_ci		snd_soc_dapm_get_volsw, class_w_put_volsw)
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int class_w_put_volsw(struct snd_kcontrol *kcontrol,
69462306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
69762306a36Sopenharmony_ci	int ret;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	wm_hubs_update_class_w(component);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return ret;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci#define WM_HUBS_ENUM_W(xname, xenum) \
70762306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
70862306a36Sopenharmony_ci	.info = snd_soc_info_enum_double, \
70962306a36Sopenharmony_ci	.get = snd_soc_dapm_get_enum_double, \
71062306a36Sopenharmony_ci	.put = class_w_put_double, \
71162306a36Sopenharmony_ci	.private_value = (unsigned long)&xenum }
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic int class_w_put_double(struct snd_kcontrol *kcontrol,
71462306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
71762306a36Sopenharmony_ci	int ret;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	wm_hubs_update_class_w(component);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return ret;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic const char *hp_mux_text[] = {
72762306a36Sopenharmony_ci	"Mixer",
72862306a36Sopenharmony_ci	"DAC",
72962306a36Sopenharmony_ci};
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(hpl_enum,
73262306a36Sopenharmony_ci			    WM8993_OUTPUT_MIXER1, 8, hp_mux_text);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ciconst struct snd_kcontrol_new wm_hubs_hpl_mux =
73562306a36Sopenharmony_ci	WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
73662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(hpr_enum,
73962306a36Sopenharmony_ci			    WM8993_OUTPUT_MIXER2, 8, hp_mux_text);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ciconst struct snd_kcontrol_new wm_hubs_hpr_mux =
74262306a36Sopenharmony_ci	WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
74362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic const struct snd_kcontrol_new in1l_pga[] = {
74662306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
74762306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
74862306a36Sopenharmony_ci};
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic const struct snd_kcontrol_new in1r_pga[] = {
75162306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0),
75262306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0),
75362306a36Sopenharmony_ci};
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic const struct snd_kcontrol_new in2l_pga[] = {
75662306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0),
75762306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0),
75862306a36Sopenharmony_ci};
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic const struct snd_kcontrol_new in2r_pga[] = {
76162306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0),
76262306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0),
76362306a36Sopenharmony_ci};
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixinl[] = {
76662306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0),
76762306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0),
76862306a36Sopenharmony_ci};
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixinr[] = {
77162306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0),
77262306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
77362306a36Sopenharmony_ci};
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic const struct snd_kcontrol_new left_output_mixer[] = {
77662306a36Sopenharmony_ciWM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
77762306a36Sopenharmony_ciWM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
77862306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
77962306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
78062306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
78162306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
78262306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
78362306a36Sopenharmony_ciWM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic const struct snd_kcontrol_new right_output_mixer[] = {
78762306a36Sopenharmony_ciWM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
78862306a36Sopenharmony_ciWM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
78962306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
79062306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
79162306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
79262306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
79362306a36Sopenharmony_ciWM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
79462306a36Sopenharmony_ciWM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
79562306a36Sopenharmony_ci};
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic const struct snd_kcontrol_new earpiece_mixer[] = {
79862306a36Sopenharmony_ciSOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0),
79962306a36Sopenharmony_ciSOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0),
80062306a36Sopenharmony_ciSOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0),
80162306a36Sopenharmony_ci};
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic const struct snd_kcontrol_new left_speaker_boost[] = {
80462306a36Sopenharmony_ciSOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0),
80562306a36Sopenharmony_ciSOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0),
80662306a36Sopenharmony_ciSOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0),
80762306a36Sopenharmony_ci};
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic const struct snd_kcontrol_new right_speaker_boost[] = {
81062306a36Sopenharmony_ciSOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0),
81162306a36Sopenharmony_ciSOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0),
81262306a36Sopenharmony_ciSOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0),
81362306a36Sopenharmony_ci};
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic const struct snd_kcontrol_new line1_mix[] = {
81662306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0),
81762306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0),
81862306a36Sopenharmony_ciSOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
81962306a36Sopenharmony_ci};
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic const struct snd_kcontrol_new line1n_mix[] = {
82262306a36Sopenharmony_ciSOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0),
82362306a36Sopenharmony_ciSOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0),
82462306a36Sopenharmony_ci};
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic const struct snd_kcontrol_new line1p_mix[] = {
82762306a36Sopenharmony_ciSOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
82862306a36Sopenharmony_ci};
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic const struct snd_kcontrol_new line2_mix[] = {
83162306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0),
83262306a36Sopenharmony_ciSOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0),
83362306a36Sopenharmony_ciSOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
83462306a36Sopenharmony_ci};
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic const struct snd_kcontrol_new line2n_mix[] = {
83762306a36Sopenharmony_ciSOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0),
83862306a36Sopenharmony_ciSOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0),
83962306a36Sopenharmony_ci};
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic const struct snd_kcontrol_new line2p_mix[] = {
84262306a36Sopenharmony_ciSOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
84362306a36Sopenharmony_ci};
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
84662306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1LN"),
84762306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1LP"),
84862306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2LN"),
84962306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2LP:VXRN"),
85062306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1RN"),
85162306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1RP"),
85262306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2RN"),
85362306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2RP:VXRP"),
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0,
85662306a36Sopenharmony_ci		    micbias_event, SND_SOC_DAPM_POST_PMU),
85762306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0,
85862306a36Sopenharmony_ci		    micbias_event, SND_SOC_DAPM_POST_PMU),
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
86162306a36Sopenharmony_ci		   in1l_pga, ARRAY_SIZE(in1l_pga)),
86262306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
86362306a36Sopenharmony_ci		   in1r_pga, ARRAY_SIZE(in1r_pga)),
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
86662306a36Sopenharmony_ci		   in2l_pga, ARRAY_SIZE(in2l_pga)),
86762306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
86862306a36Sopenharmony_ci		   in2r_pga, ARRAY_SIZE(in2r_pga)),
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
87162306a36Sopenharmony_ci		   mixinl, ARRAY_SIZE(mixinl)),
87262306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
87362306a36Sopenharmony_ci		   mixinr, ARRAY_SIZE(mixinr)),
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
87662306a36Sopenharmony_ci		   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
87762306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
87862306a36Sopenharmony_ci		   right_output_mixer, ARRAY_SIZE(right_output_mixer)),
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
88162306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
88462306a36Sopenharmony_ci		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
88562306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
88662306a36Sopenharmony_ci		       hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
88962306a36Sopenharmony_ci		   earpiece_mixer, ARRAY_SIZE(earpiece_mixer)),
89062306a36Sopenharmony_ciSND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0,
89162306a36Sopenharmony_ci		   NULL, 0, earpiece_event,
89262306a36Sopenharmony_ci		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0,
89562306a36Sopenharmony_ci		   left_speaker_boost, ARRAY_SIZE(left_speaker_boost)),
89662306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0,
89762306a36Sopenharmony_ci		   right_speaker_boost, ARRAY_SIZE(right_speaker_boost)),
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("TSHUT", WM8993_POWER_MANAGEMENT_2, 14, 0, NULL, 0),
90062306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0,
90162306a36Sopenharmony_ci		     NULL, 0),
90262306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0,
90362306a36Sopenharmony_ci		     NULL, 0),
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
90662306a36Sopenharmony_ci		   line1_mix, ARRAY_SIZE(line1_mix)),
90762306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
90862306a36Sopenharmony_ci		   line2_mix, ARRAY_SIZE(line2_mix)),
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0,
91162306a36Sopenharmony_ci		   line1n_mix, ARRAY_SIZE(line1n_mix)),
91262306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0,
91362306a36Sopenharmony_ci		   line1p_mix, ARRAY_SIZE(line1p_mix)),
91462306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0,
91562306a36Sopenharmony_ci		   line2n_mix, ARRAY_SIZE(line2n_mix)),
91662306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0,
91762306a36Sopenharmony_ci		   line2p_mix, ARRAY_SIZE(line2p_mix)),
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV_E("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0,
92062306a36Sopenharmony_ci		       NULL, 0, lineout_event,
92162306a36Sopenharmony_ci		     SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
92262306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV_E("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0,
92362306a36Sopenharmony_ci		       NULL, 0, lineout_event,
92462306a36Sopenharmony_ci		       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
92562306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV_E("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0,
92662306a36Sopenharmony_ci		       NULL, 0, lineout_event,
92762306a36Sopenharmony_ci		       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
92862306a36Sopenharmony_ciSND_SOC_DAPM_OUT_DRV_E("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0,
92962306a36Sopenharmony_ci		       NULL, 0, lineout_event,
93062306a36Sopenharmony_ci		       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTLP"),
93362306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTLN"),
93462306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTRP"),
93562306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTRN"),
93662306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUT1L"),
93762306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUT1R"),
93862306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUT2P"),
93962306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUT2N"),
94062306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUT1P"),
94162306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUT1N"),
94262306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUT2P"),
94362306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUT2N"),
94462306a36Sopenharmony_ci};
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic const struct snd_soc_dapm_route analogue_routes[] = {
94762306a36Sopenharmony_ci	{ "MICBIAS1", NULL, "CLK_SYS" },
94862306a36Sopenharmony_ci	{ "MICBIAS2", NULL, "CLK_SYS" },
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	{ "IN1L PGA", "IN1LP Switch", "IN1LP" },
95162306a36Sopenharmony_ci	{ "IN1L PGA", "IN1LN Switch", "IN1LN" },
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	{ "IN1L PGA", NULL, "VMID" },
95462306a36Sopenharmony_ci	{ "IN1R PGA", NULL, "VMID" },
95562306a36Sopenharmony_ci	{ "IN2L PGA", NULL, "VMID" },
95662306a36Sopenharmony_ci	{ "IN2R PGA", NULL, "VMID" },
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
95962306a36Sopenharmony_ci	{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	{ "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
96262306a36Sopenharmony_ci	{ "IN2L PGA", "IN2LN Switch", "IN2LN" },
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	{ "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
96562306a36Sopenharmony_ci	{ "IN2R PGA", "IN2RN Switch", "IN2RN" },
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	{ "Direct Voice", NULL, "IN2LP:VXRN" },
96862306a36Sopenharmony_ci	{ "Direct Voice", NULL, "IN2RP:VXRP" },
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	{ "MIXINL", "IN1L Switch", "IN1L PGA" },
97162306a36Sopenharmony_ci	{ "MIXINL", "IN2L Switch", "IN2L PGA" },
97262306a36Sopenharmony_ci	{ "MIXINL", NULL, "Direct Voice" },
97362306a36Sopenharmony_ci	{ "MIXINL", NULL, "IN1LP" },
97462306a36Sopenharmony_ci	{ "MIXINL", NULL, "Left Output Mixer" },
97562306a36Sopenharmony_ci	{ "MIXINL", NULL, "VMID" },
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	{ "MIXINR", "IN1R Switch", "IN1R PGA" },
97862306a36Sopenharmony_ci	{ "MIXINR", "IN2R Switch", "IN2R PGA" },
97962306a36Sopenharmony_ci	{ "MIXINR", NULL, "Direct Voice" },
98062306a36Sopenharmony_ci	{ "MIXINR", NULL, "IN1RP" },
98162306a36Sopenharmony_ci	{ "MIXINR", NULL, "Right Output Mixer" },
98262306a36Sopenharmony_ci	{ "MIXINR", NULL, "VMID" },
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	{ "ADCL", NULL, "MIXINL" },
98562306a36Sopenharmony_ci	{ "ADCR", NULL, "MIXINR" },
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	{ "Left Output Mixer", "Left Input Switch", "MIXINL" },
98862306a36Sopenharmony_ci	{ "Left Output Mixer", "Right Input Switch", "MIXINR" },
98962306a36Sopenharmony_ci	{ "Left Output Mixer", "IN2RN Switch", "IN2RN" },
99062306a36Sopenharmony_ci	{ "Left Output Mixer", "IN2LN Switch", "IN2LN" },
99162306a36Sopenharmony_ci	{ "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
99262306a36Sopenharmony_ci	{ "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
99362306a36Sopenharmony_ci	{ "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	{ "Right Output Mixer", "Left Input Switch", "MIXINL" },
99662306a36Sopenharmony_ci	{ "Right Output Mixer", "Right Input Switch", "MIXINR" },
99762306a36Sopenharmony_ci	{ "Right Output Mixer", "IN2LN Switch", "IN2LN" },
99862306a36Sopenharmony_ci	{ "Right Output Mixer", "IN2RN Switch", "IN2RN" },
99962306a36Sopenharmony_ci	{ "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
100062306a36Sopenharmony_ci	{ "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
100162306a36Sopenharmony_ci	{ "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	{ "Left Output PGA", NULL, "Left Output Mixer" },
100462306a36Sopenharmony_ci	{ "Left Output PGA", NULL, "TOCLK" },
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	{ "Right Output PGA", NULL, "Right Output Mixer" },
100762306a36Sopenharmony_ci	{ "Right Output PGA", NULL, "TOCLK" },
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	{ "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" },
101062306a36Sopenharmony_ci	{ "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
101162306a36Sopenharmony_ci	{ "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	{ "Earpiece Driver", NULL, "VMID" },
101462306a36Sopenharmony_ci	{ "Earpiece Driver", NULL, "Earpiece Mixer" },
101562306a36Sopenharmony_ci	{ "HPOUT2N", NULL, "Earpiece Driver" },
101662306a36Sopenharmony_ci	{ "HPOUT2P", NULL, "Earpiece Driver" },
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	{ "SPKL", "Input Switch", "MIXINL" },
101962306a36Sopenharmony_ci	{ "SPKL", "IN1LP Switch", "IN1LP" },
102062306a36Sopenharmony_ci	{ "SPKL", "Output Switch", "Left Output PGA" },
102162306a36Sopenharmony_ci	{ "SPKL", NULL, "TOCLK" },
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	{ "SPKR", "Input Switch", "MIXINR" },
102462306a36Sopenharmony_ci	{ "SPKR", "IN1RP Switch", "IN1RP" },
102562306a36Sopenharmony_ci	{ "SPKR", "Output Switch", "Right Output PGA" },
102662306a36Sopenharmony_ci	{ "SPKR", NULL, "TOCLK" },
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	{ "SPKL Boost", "Direct Voice Switch", "Direct Voice" },
102962306a36Sopenharmony_ci	{ "SPKL Boost", "SPKL Switch", "SPKL" },
103062306a36Sopenharmony_ci	{ "SPKL Boost", "SPKR Switch", "SPKR" },
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	{ "SPKR Boost", "Direct Voice Switch", "Direct Voice" },
103362306a36Sopenharmony_ci	{ "SPKR Boost", "SPKR Switch", "SPKR" },
103462306a36Sopenharmony_ci	{ "SPKR Boost", "SPKL Switch", "SPKL" },
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	{ "SPKL Driver", NULL, "VMID" },
103762306a36Sopenharmony_ci	{ "SPKL Driver", NULL, "SPKL Boost" },
103862306a36Sopenharmony_ci	{ "SPKL Driver", NULL, "CLK_SYS" },
103962306a36Sopenharmony_ci	{ "SPKL Driver", NULL, "TSHUT" },
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	{ "SPKR Driver", NULL, "VMID" },
104262306a36Sopenharmony_ci	{ "SPKR Driver", NULL, "SPKR Boost" },
104362306a36Sopenharmony_ci	{ "SPKR Driver", NULL, "CLK_SYS" },
104462306a36Sopenharmony_ci	{ "SPKR Driver", NULL, "TSHUT" },
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	{ "SPKOUTLP", NULL, "SPKL Driver" },
104762306a36Sopenharmony_ci	{ "SPKOUTLN", NULL, "SPKL Driver" },
104862306a36Sopenharmony_ci	{ "SPKOUTRP", NULL, "SPKR Driver" },
104962306a36Sopenharmony_ci	{ "SPKOUTRN", NULL, "SPKR Driver" },
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	{ "Left Headphone Mux", "Mixer", "Left Output PGA" },
105262306a36Sopenharmony_ci	{ "Right Headphone Mux", "Mixer", "Right Output PGA" },
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	{ "Headphone PGA", NULL, "Left Headphone Mux" },
105562306a36Sopenharmony_ci	{ "Headphone PGA", NULL, "Right Headphone Mux" },
105662306a36Sopenharmony_ci	{ "Headphone PGA", NULL, "VMID" },
105762306a36Sopenharmony_ci	{ "Headphone PGA", NULL, "CLK_SYS" },
105862306a36Sopenharmony_ci	{ "Headphone PGA", NULL, "Headphone Supply" },
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	{ "HPOUT1L", NULL, "Headphone PGA" },
106162306a36Sopenharmony_ci	{ "HPOUT1R", NULL, "Headphone PGA" },
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	{ "LINEOUT1N Driver", NULL, "VMID" },
106462306a36Sopenharmony_ci	{ "LINEOUT1P Driver", NULL, "VMID" },
106562306a36Sopenharmony_ci	{ "LINEOUT2N Driver", NULL, "VMID" },
106662306a36Sopenharmony_ci	{ "LINEOUT2P Driver", NULL, "VMID" },
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	{ "LINEOUT1N", NULL, "LINEOUT1N Driver" },
106962306a36Sopenharmony_ci	{ "LINEOUT1P", NULL, "LINEOUT1P Driver" },
107062306a36Sopenharmony_ci	{ "LINEOUT2N", NULL, "LINEOUT2N Driver" },
107162306a36Sopenharmony_ci	{ "LINEOUT2P", NULL, "LINEOUT2P Driver" },
107262306a36Sopenharmony_ci};
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route lineout1_diff_routes[] = {
107562306a36Sopenharmony_ci	{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
107662306a36Sopenharmony_ci	{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
107762306a36Sopenharmony_ci	{ "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
108062306a36Sopenharmony_ci	{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
108162306a36Sopenharmony_ci};
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route lineout1_se_routes[] = {
108462306a36Sopenharmony_ci	{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
108562306a36Sopenharmony_ci	{ "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	{ "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
109062306a36Sopenharmony_ci	{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
109162306a36Sopenharmony_ci};
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route lineout2_diff_routes[] = {
109462306a36Sopenharmony_ci	{ "LINEOUT2 Mixer", "IN1L Switch", "IN1L PGA" },
109562306a36Sopenharmony_ci	{ "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" },
109662306a36Sopenharmony_ci	{ "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
109962306a36Sopenharmony_ci	{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
110062306a36Sopenharmony_ci};
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic const struct snd_soc_dapm_route lineout2_se_routes[] = {
110362306a36Sopenharmony_ci	{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
110462306a36Sopenharmony_ci	{ "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	{ "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
110962306a36Sopenharmony_ci	{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
111062306a36Sopenharmony_ci};
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ciint wm_hubs_add_analogue_controls(struct snd_soc_component *component)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	/* Latch volume update bits & default ZC on */
111762306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
111862306a36Sopenharmony_ci			    WM8993_IN1_VU, WM8993_IN1_VU);
111962306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME,
112062306a36Sopenharmony_ci			    WM8993_IN1_VU, WM8993_IN1_VU);
112162306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_LEFT_LINE_INPUT_3_4_VOLUME,
112262306a36Sopenharmony_ci			    WM8993_IN2_VU, WM8993_IN2_VU);
112362306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
112462306a36Sopenharmony_ci			    WM8993_IN2_VU, WM8993_IN2_VU);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_LEFT,
112762306a36Sopenharmony_ci			    WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
112862306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_RIGHT,
112962306a36Sopenharmony_ci			    WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_LEFT_OUTPUT_VOLUME,
113262306a36Sopenharmony_ci			    WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
113362306a36Sopenharmony_ci			    WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
113462306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_RIGHT_OUTPUT_VOLUME,
113562306a36Sopenharmony_ci			    WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
113662306a36Sopenharmony_ci			    WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_LEFT_OPGA_VOLUME,
113962306a36Sopenharmony_ci			    WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
114062306a36Sopenharmony_ci			    WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
114162306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_RIGHT_OPGA_VOLUME,
114262306a36Sopenharmony_ci			    WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
114362306a36Sopenharmony_ci			    WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	snd_soc_add_component_controls(component, analogue_snd_controls,
114662306a36Sopenharmony_ci			     ARRAY_SIZE(analogue_snd_controls));
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	snd_soc_dapm_new_controls(dapm, analogue_dapm_widgets,
114962306a36Sopenharmony_ci				  ARRAY_SIZE(analogue_dapm_widgets));
115062306a36Sopenharmony_ci	return 0;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ciint wm_hubs_add_analogue_routes(struct snd_soc_component *component,
115562306a36Sopenharmony_ci				int lineout1_diff, int lineout2_diff)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
115862306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	hubs->component = component;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	INIT_LIST_HEAD(&hubs->dcs_cache);
116362306a36Sopenharmony_ci	init_completion(&hubs->dcs_done);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	snd_soc_dapm_add_routes(dapm, analogue_routes,
116662306a36Sopenharmony_ci				ARRAY_SIZE(analogue_routes));
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (lineout1_diff)
116962306a36Sopenharmony_ci		snd_soc_dapm_add_routes(dapm,
117062306a36Sopenharmony_ci					lineout1_diff_routes,
117162306a36Sopenharmony_ci					ARRAY_SIZE(lineout1_diff_routes));
117262306a36Sopenharmony_ci	else
117362306a36Sopenharmony_ci		snd_soc_dapm_add_routes(dapm,
117462306a36Sopenharmony_ci					lineout1_se_routes,
117562306a36Sopenharmony_ci					ARRAY_SIZE(lineout1_se_routes));
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (lineout2_diff)
117862306a36Sopenharmony_ci		snd_soc_dapm_add_routes(dapm,
117962306a36Sopenharmony_ci					lineout2_diff_routes,
118062306a36Sopenharmony_ci					ARRAY_SIZE(lineout2_diff_routes));
118162306a36Sopenharmony_ci	else
118262306a36Sopenharmony_ci		snd_soc_dapm_add_routes(dapm,
118362306a36Sopenharmony_ci					lineout2_se_routes,
118462306a36Sopenharmony_ci					ARRAY_SIZE(lineout2_se_routes));
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	return 0;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ciint wm_hubs_handle_analogue_pdata(struct snd_soc_component *component,
119162306a36Sopenharmony_ci				  int lineout1_diff, int lineout2_diff,
119262306a36Sopenharmony_ci				  int lineout1fb, int lineout2fb,
119362306a36Sopenharmony_ci				  int jd_scthr, int jd_thr,
119462306a36Sopenharmony_ci				  int micbias1_delay, int micbias2_delay,
119562306a36Sopenharmony_ci				  int micbias1_lvl, int micbias2_lvl)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	hubs->lineout1_se = !lineout1_diff;
120062306a36Sopenharmony_ci	hubs->lineout2_se = !lineout2_diff;
120162306a36Sopenharmony_ci	hubs->micb1_delay = micbias1_delay;
120262306a36Sopenharmony_ci	hubs->micb2_delay = micbias2_delay;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (!lineout1_diff)
120562306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_LINE_MIXER1,
120662306a36Sopenharmony_ci				    WM8993_LINEOUT1_MODE,
120762306a36Sopenharmony_ci				    WM8993_LINEOUT1_MODE);
120862306a36Sopenharmony_ci	if (!lineout2_diff)
120962306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_LINE_MIXER2,
121062306a36Sopenharmony_ci				    WM8993_LINEOUT2_MODE,
121162306a36Sopenharmony_ci				    WM8993_LINEOUT2_MODE);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (!lineout1_diff && !lineout2_diff)
121462306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_ANTIPOP1,
121562306a36Sopenharmony_ci				    WM8993_LINEOUT_VMID_BUF_ENA,
121662306a36Sopenharmony_ci				    WM8993_LINEOUT_VMID_BUF_ENA);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (lineout1fb)
121962306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL,
122062306a36Sopenharmony_ci				    WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (lineout2fb)
122362306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL,
122462306a36Sopenharmony_ci				    WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (!hubs->micd_scthr)
122762306a36Sopenharmony_ci		return 0;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_MICBIAS,
123062306a36Sopenharmony_ci			    WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
123162306a36Sopenharmony_ci			    WM8993_MICB1_LVL | WM8993_MICB2_LVL,
123262306a36Sopenharmony_ci			    jd_scthr << WM8993_JD_SCTHR_SHIFT |
123362306a36Sopenharmony_ci			    jd_thr << WM8993_JD_THR_SHIFT |
123462306a36Sopenharmony_ci			    micbias1_lvl |
123562306a36Sopenharmony_ci			    micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_civoid wm_hubs_vmid_ena(struct snd_soc_component *component)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
124462306a36Sopenharmony_ci	int val = 0;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	if (hubs->lineout1_se)
124762306a36Sopenharmony_ci		val |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (hubs->lineout2_se)
125062306a36Sopenharmony_ci		val |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* Enable the line outputs while we power up */
125362306a36Sopenharmony_ci	snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3, val, val);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_vmid_ena);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_civoid wm_hubs_set_bias_level(struct snd_soc_component *component,
125862306a36Sopenharmony_ci			    enum snd_soc_bias_level level)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
126162306a36Sopenharmony_ci	int mask, val;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	switch (level) {
126462306a36Sopenharmony_ci	case SND_SOC_BIAS_STANDBY:
126562306a36Sopenharmony_ci		/* Clamp the inputs to VMID while we ramp to charge caps */
126662306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG,
126762306a36Sopenharmony_ci				    WM8993_INPUTS_CLAMP, WM8993_INPUTS_CLAMP);
126862306a36Sopenharmony_ci		break;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	case SND_SOC_BIAS_ON:
127162306a36Sopenharmony_ci		/* Turn off any unneeded single ended outputs */
127262306a36Sopenharmony_ci		val = 0;
127362306a36Sopenharmony_ci		mask = 0;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci		if (hubs->lineout1_se)
127662306a36Sopenharmony_ci			mask |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		if (hubs->lineout2_se)
127962306a36Sopenharmony_ci			mask |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		if (hubs->lineout1_se && hubs->lineout1n_ena)
128262306a36Sopenharmony_ci			val |= WM8993_LINEOUT1N_ENA;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci		if (hubs->lineout1_se && hubs->lineout1p_ena)
128562306a36Sopenharmony_ci			val |= WM8993_LINEOUT1P_ENA;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		if (hubs->lineout2_se && hubs->lineout2n_ena)
128862306a36Sopenharmony_ci			val |= WM8993_LINEOUT2N_ENA;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		if (hubs->lineout2_se && hubs->lineout2p_ena)
129162306a36Sopenharmony_ci			val |= WM8993_LINEOUT2P_ENA;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3,
129462306a36Sopenharmony_ci				    mask, val);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		/* Remove the input clamps */
129762306a36Sopenharmony_ci		snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG,
129862306a36Sopenharmony_ci				    WM8993_INPUTS_CLAMP, 0);
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	default:
130262306a36Sopenharmony_ci		break;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_hubs_set_bias_level);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ciMODULE_DESCRIPTION("Shared support for Wolfson hubs products");
130862306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
130962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1310