diff options
author | Philippe Langlais <philippe.langlais@stericsson.com> | 2011-10-20 10:33:31 +0200 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:04:41 +0200 |
commit | 9d5ac7304583952239cf71a3b3a58f7d3cc742b3 (patch) | |
tree | fb2494fe0d73d0aca367868be6762890dbce5227 | |
parent | 76e10d158efb6d4516018846f60c2ab5501900bc (diff) |
misc: Add audio_io_dev driver
Signed-off-by: Robert Marklund <robert.marklund@stericsson.com>
-rw-r--r-- | arch/arm/mach-ux500/include/mach/ste_audio_io_ioctl.h | 224 | ||||
-rw-r--r-- | arch/arm/mach-ux500/include/mach/ste_audio_io_vibrator.h | 37 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/Kconfig | 11 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/Makefile | 9 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h | 349 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_core.c | 1464 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_core.h | 133 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_dev.c | 738 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_dev.h | 32 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_func.c | 4371 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_func.h | 359 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c | 189 | ||||
-rw-r--r-- | drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h | 50 |
13 files changed, 7966 insertions, 0 deletions
diff --git a/arch/arm/mach-ux500/include/mach/ste_audio_io_ioctl.h b/arch/arm/mach-ux500/include/mach/ste_audio_io_ioctl.h new file mode 100644 index 00000000000..73dc9d9ee7e --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/ste_audio_io_ioctl.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef _AUDIOIO_IOCTL_H_ +#define _AUDIOIO_IOCTL_H_ + + +#define AUDIOIO_IOC_MAGIC 'N' +#define AUDIOIO_READ_REGISTER _IOWR(AUDIOIO_IOC_MAGIC, 1,\ + struct audioio_data_t) +#define AUDIOIO_WRITE_REGISTER _IOW(AUDIOIO_IOC_MAGIC, 2,\ + struct audioio_data_t) +#define AUDIOIO_PWR_CTRL_TRNSDR _IOW(AUDIOIO_IOC_MAGIC, 3,\ + struct audioio_pwr_ctrl_t) +#define AUDIOIO_PWR_STS_TRNSDR _IOR(AUDIOIO_IOC_MAGIC, 4,\ + struct audioio_pwr_ctrl_t) +#define AUDIOIO_LOOP_CTRL _IOW(AUDIOIO_IOC_MAGIC, 5,\ + struct audioio_loop_ctrl_t) +#define AUDIOIO_LOOP_STS _IOR(AUDIOIO_IOC_MAGIC, 6,\ + struct audioio_loop_ctrl_t) +#define AUDIOIO_GET_TRNSDR_GAIN_CAPABILITY _IOR(AUDIOIO_IOC_MAGIC, 7,\ + struct audioio_get_gain_t) +#define AUDIOIO_GAIN_CAP_LOOP _IOR(AUDIOIO_IOC_MAGIC, 8,\ + struct audioio_gain_loop_t) +#define AUDIOIO_SUPPORT_LOOP _IOR(AUDIOIO_IOC_MAGIC, 9,\ + struct audioio_support_loop_t) +#define AUDIOIO_GAIN_DESC_TRNSDR _IOR(AUDIOIO_IOC_MAGIC, 10,\ + struct audioio_gain_desc_trnsdr_t) +#define AUDIOIO_GAIN_CTRL_TRNSDR _IOW(AUDIOIO_IOC_MAGIC, 11,\ + struct audioio_gain_ctrl_trnsdr_t) +#define AUDIOIO_GAIN_QUERY_TRNSDR _IOR(AUDIOIO_IOC_MAGIC, 12,\ + struct audioio_gain_ctrl_trnsdr_t) +#define AUDIOIO_MUTE_CTRL_TRNSDR _IOW(AUDIOIO_IOC_MAGIC, 13,\ + struct audioio_mute_trnsdr_t) +#define AUDIOIO_MUTE_STS_TRNSDR _IOR(AUDIOIO_IOC_MAGIC, 14,\ + struct audioio_mute_trnsdr_t) +#define AUDIOIO_FADE_CTRL _IOW(AUDIOIO_IOC_MAGIC, 15,\ + struct audioio_fade_ctrl_t) +#define AUDIOIO_BURST_CTRL _IOW(AUDIOIO_IOC_MAGIC, 16,\ + struct audioio_burst_ctrl_t) +#define AUDIOIO_READ_ALL_ACODEC_REGS_CTRL _IOW(AUDIOIO_IOC_MAGIC, 17,\ + struct audioio_read_all_acodec_reg_ctrl_t) +#define AUDIOIO_FSBITCLK_CTRL _IOW(AUDIOIO_IOC_MAGIC, 18,\ + struct audioio_fsbitclk_ctrl_t) +#define AUDIOIO_PSEUDOBURST_CTRL _IOW(AUDIOIO_IOC_MAGIC, 19,\ + struct audioio_pseudoburst_ctrl_t) +#define AUDIOIO_AUDIOCODEC_PWR_CTRL _IOW(AUDIOIO_IOC_MAGIC, 20, \ + struct audioio_acodec_pwr_ctrl_t) +#define AUDIOIO_FIR_COEFFS_CTRL _IOW(AUDIOIO_IOC_MAGIC, 21, \ + struct audioio_fir_coefficients_t) +#define AUDIOIO_LOOP_GAIN_DESC_TRNSDR _IOR(AUDIOIO_IOC_MAGIC, 22,\ + struct audioio_gain_desc_trnsdr_t) +/* audio codec channel ids */ +#define EAR_CH 0 +#define HS_CH 1 +#define IHF_CH 2 +#define VIBL_CH 3 +#define VIBR_CH 4 +#define MIC1A_CH 5 +#define MIC1B_CH 6 +#define MIC2_CH 7 +#define LIN_CH 8 +#define DMIC12_CH 9 +#define DMIC34_CH 10 +#define DMIC56_CH 11 +#define MULTI_MIC_CH 12 +#define FMRX_CH 13 +#define FMTX_CH 14 +#define BLUETOOTH_CH 15 + +#define FIRST_CH EAR_CH +#define LAST_CH BLUETOOTH_CH + +#define MAX_NO_TRANSDUCERS 16 +#define STE_AUDIOIO_MAX_COEFFICIENTS 128 +#define MAX_NO_OF_LOOPS 19 + +#define AUDIOIO_TRUE 1 +#define AUDIOIO_FALSE 0 + +enum AUDIOIO_COMMON_SWITCH { + AUDIOIO_COMMON_OFF = 0, + AUDIOIO_COMMON_ON, + AUDIOIO_COMMON_ALLCHANNEL_UNSUPPORTED = 0xFFFF +}; + +enum AUDIOIO_HAL_HW_LOOPS { + AUDIOIO_NO_LOOP = 0x0, + AUDIOIO_SIDETONE_LOOP = 0x01, + AUDIOIO_MIC1B_TO_HFL = 0x02, + AUDIOIO_MIC1B_TO_HFR = 0x04, + AUDIOIO_MIC1B_TO_EAR = 0x08, + AUDIOIO_MIC1A_TO_HSL = 0x10, + AUDIOIO_MIC1A_TO_HSR = 0x20, + AUDIOIO_MIC1A_TO_HSR_HSL = 0x40, + AUDIOIO_LINEIN_TO_HF = 0x80, + AUDIOIO_DMIC12_TO_HSR_HSL = 0x100, + AUDIOIO_DIC34_TO_HSR_HSL = 0x200, + AUDIOIO_DIC56_TO_HSR_HSL = 0x400, + AUDIOIO_DMIC12_TO_ST = 0x800, + AUDIOIO_DMIC34_TO_ST = 0x1000, + AUDIOIO_DMIC56_TO_ST = 0x2000, + AUDIOIO_ANC_LOOP = 0x4000, + AUDIOIO_LININ_HS = 0x8000, + AUDIOIO_LININL_HSL = 0x10000, + AUDIOIO_LININ_HSR = 0x20000 +}; + + +enum AUDIOIO_FADE_PERIOD { + e_FADE_00, + e_FADE_01, + e_FADE_10, + e_FADE_11 +}; + +enum AUDIOIO_CH_INDEX { + e_CHANNEL_1 = 0x01, + e_CHANNEL_2 = 0x02, + e_CHANNEL_3 = 0x04, + e_CHANNEL_4 = 0x08, + e_CHANNEL_ALL = 0x0f +}; + +struct audioio_data_t { + unsigned char block; + unsigned char addr; + unsigned char data; +}; + +struct audioio_pwr_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; + int channel_type; + enum AUDIOIO_CH_INDEX channel_index; +}; + +struct audioio_acodec_pwr_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; +}; + +struct audioio_loop_ctrl_t { + enum AUDIOIO_HAL_HW_LOOPS hw_loop; + enum AUDIOIO_COMMON_SWITCH ctrl_switch; + int channel_type; + enum AUDIOIO_CH_INDEX channel_index; + int loop_gain; +}; + +struct audioio_get_gain_t { + unsigned int num_channels; + unsigned short max_num_gain; +}; + +struct audioio_gain_loop_t { + int channel_type; + unsigned short num_loop; + unsigned short max_gains; +}; + +struct audioio_support_loop_t { + int channel_type; + unsigned short spprtd_loop_index; +}; + +struct audioio_gain_desc_trnsdr_t { + enum AUDIOIO_CH_INDEX channel_index; + int channel_type; + unsigned short gain_index; + int min_gain; + int max_gain; + unsigned int gain_step; +}; + +struct audioio_gain_ctrl_trnsdr_t { + enum AUDIOIO_CH_INDEX channel_index; + int channel_type; + unsigned short gain_index; + int gain_value; + unsigned int linear; +}; + +struct audioio_mute_trnsdr_t { + int channel_type; + enum AUDIOIO_CH_INDEX channel_index; + enum AUDIOIO_COMMON_SWITCH ctrl_switch; +}; + +struct audioio_fade_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; + enum AUDIOIO_FADE_PERIOD fade_period; + int channel_type; + enum AUDIOIO_CH_INDEX channel_index; +}; + +struct audioio_burst_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; + int channel_type; + int burst_fifo_interrupt_sample_count; + int burst_fifo_length;/* BFIFOTx */ + int burst_fifo_switch_frame; + int burst_fifo_sample_number; +}; + +struct audioio_read_all_acodec_reg_ctrl_t { + unsigned char data[200]; +}; + +struct audioio_fsbitclk_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; +}; + +struct audioio_pseudoburst_ctrl_t { + enum AUDIOIO_COMMON_SWITCH ctrl_switch; +}; + +struct audioio_fir_coefficients_t { + unsigned char start_addr; + unsigned short coefficients[STE_AUDIOIO_MAX_COEFFICIENTS]; +}; + +#endif diff --git a/arch/arm/mach-ux500/include/mach/ste_audio_io_vibrator.h b/arch/arm/mach-ux500/include/mach/ste_audio_io_vibrator.h new file mode 100644 index 00000000000..6b6a558e90a --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/ste_audio_io_vibrator.h @@ -0,0 +1,37 @@ +/* +* Overview: +* Header File defining vibrator kernel space interface +* +* Copyright (C) 2010 ST Ericsson +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef _STE_AUDIO_IO_VIBRATOR_H_ +#define _STE_AUDIO_IO_VIBRATOR_H_ + +/* Client definitions which can use vibrator, defined as bitmask */ +#define STE_AUDIOIO_CLIENT_AUDIO_L 1 +#define STE_AUDIOIO_CLIENT_AUDIO_R 2 +#define STE_AUDIOIO_CLIENT_FF_VIBRA 4 +#define STE_AUDIOIO_CLIENT_TIMED_VIBRA 8 + +/* + * Define vibrator's maximum speed allowed + * Duty cycle supported by vibrator's PWM is 0-100 + */ +#define STE_AUDIOIO_VIBRATOR_MAX_SPEED 100 + +/* Vibrator speed structure */ +struct ste_vibra_speed { + unsigned char positive; + unsigned char negative; +}; + +/* Vibrator control function - uses PWM source */ +int ste_audioio_vibrator_pwm_control(int client, + struct ste_vibra_speed left_speed, struct ste_vibra_speed right_speed); + +#endif diff --git a/drivers/misc/audio_io_dev/Kconfig b/drivers/misc/audio_io_dev/Kconfig new file mode 100644 index 00000000000..57bb77172f7 --- /dev/null +++ b/drivers/misc/audio_io_dev/Kconfig @@ -0,0 +1,11 @@ +# +# AB8500 Audio IO Device Driver configuration +# +config STE_AUDIO_IO_DEV + bool "AB8500 Audio IO device driver" + depends on ARCH_U8500 && AB8500_CORE && STM_MSP_I2S + default y + ---help--- + If you say Y here, you will enable the AB8500 Audio IO device driver. + + If unsure, say N. diff --git a/drivers/misc/audio_io_dev/Makefile b/drivers/misc/audio_io_dev/Makefile new file mode 100644 index 00000000000..44b21fcc573 --- /dev/null +++ b/drivers/misc/audio_io_dev/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for AB8500 device drivers +# +obj-$(CONFIG_STE_AUDIO_IO_DEV) += ste_audio_io.o +ste_audio_io-objs := ste_audio_io_dev.o\ + ste_audio_io_core.o\ + ste_audio_io_func.o\ + ste_audio_io_hwctrl_common.o + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h b/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h new file mode 100644 index 00000000000..1436430f7de --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + + +#ifndef _AUDIOIO_REG_DEFS_H_ +#define _AUDIOIO_REG_DEFS_H_ + + + /* Registers */ +#define POWER_UP_CONTROL_REG 0x0D00 +#define SOFTWARE_RESET_REG 0x0D01 +#define DIGITAL_AD_CHANNELS_ENABLE_REG 0x0D02 +#define DIGITAL_DA_CHANNELS_ENABLE_REG 0x0D03 +#define LOW_POWER_HS_EAR_CONF_REG 0x0D04 +#define LINE_IN_MIC_CONF_REG 0x0D05 +#define DMIC_ENABLE_REG 0x0D06 +#define ADC_DAC_ENABLE_REG 0x0D07 +#define ANALOG_OUTPUT_ENABLE_REG 0x0D08 +#define DIGITAL_OUTPUT_ENABLE_REG 0x0D09 +#define MUTE_HS_EAR_REG 0x0D0A +#define SHORT_CIRCUIT_DISABLE_REG 0x0D0B +#define NCP_ENABLE_HS_AUTOSTART_REG 0x0D0C +#define ENVELOPE_THRESHOLD_REG 0x0D0D +#define ENVELOPE_DECAY_TIME_REG 0x0D0E +#define VIB_DRIVER_CONF_REG 0x0D0F +#define PWM_VIBNL_CONF_REG 0x0D10 +#define PWM_VIBPL_CONF_REG 0x0D11 +#define PWM_VIBNR_CONF_REG 0x0D12 +#define PWM_VIBPR_CONF_REG 0x0D13 +#define ANALOG_MIC1_GAIN_REG 0x0D14 +#define ANALOG_MIC2_GAIN_REG 0x0D15 +#define ANALOG_HS_GAIN_REG 0x0D16 +#define ANALOG_LINE_IN_GAIN_REG 0x0D17 +#define LINE_IN_TO_HSL_GAIN_REG 0x0D18 +#define LINE_IN_TO_HSR_GAIN_REG 0x0D19 +#define AD_FILTER_CONF_REG 0x0D1A +#define IF0_IF1_MASTER_CONF_REG 0x0D1B +#define IF0_CONF_REG 0x0D1C +#define TDM_IF_BYPASS_B_FIFO_REG 0x0D1D +#define IF1_CONF_REG 0x0D1E +#define AD_ALLOCATION_TO_SLOT0_1_REG 0x0D1F +#define AD_ALLOCATION_TO_SLOT2_3_REG 0x0D20 +#define AD_ALLOCATION_TO_SLOT4_5_REG 0x0D21 +#define AD_ALLOCATION_TO_SLOT6_7_REG 0x0D22 +#define AD_ALLOCATION_TO_SLOT8_9_REG 0x0D23 +#define AD_ALLOCATION_TO_SLOT10_11_REG 0x0D24 +#define AD_ALLOCATION_TO_SLOT12_13_REG 0x0D25 +#define AD_ALLOCATION_TO_SLOT14_15_REG 0x0D26 +#define AD_ALLOCATION_TO_SLOT16_17_REG 0x0D27 +#define AD_ALLOCATION_TO_SLOT18_19_REG 0x0D28 +#define AD_ALLOCATION_TO_SLOT20_21_REG 0x0D29 +#define AD_ALLOCATION_TO_SLOT22_23_REG 0x0D2A +#define AD_ALLOCATION_TO_SLOT24_25_REG 0x0D2B +#define AD_ALLOCATION_TO_SLOT26_27_REG 0x0D2C +#define AD_ALLOCATION_TO_SLOT28_29_REG 0x0D2D +#define AD_ALLOCATION_TO_SLOT30_31_REG 0x0D2E +#define AD_SLOT_0_TO_7_TRISTATE_REG 0x0D2F +#define AD_SLOT_8_TO_15_TRISTATE_REG 0x0D30 +#define AD_SLOT_16_TO_23_TRISTATE_REG 0x0D31 +#define AD_SLOT_24_TO_31_TRISTATE_REG 0x0D32 +#define SLOT_SELECTION_TO_DA1_REG 0x0D33 +#define SLOT_SELECTION_TO_DA2_REG 0x0D34 +#define SLOT_SELECTION_TO_DA3_REG 0x0D35 +#define SLOT_SELECTION_TO_DA4_REG 0x0D36 +#define SLOT_SELECTION_TO_DA5_REG 0x0D37 +#define SLOT_SELECTION_TO_DA6_REG 0x0D38 +#define SLOT_SELECTION_TO_DA7_REG 0x0D39 +#define SLOT_SELECTION_TO_DA8_REG 0x0D3A +#define CLASS_D_EMI_PARALLEL_CONF_REG 0x0D3B +#define CLASS_D_PATH_CONTROL_REG 0x0D3C +#define CLASS_D_DITHER_CONTROL_REG 0x0D3D +#define DMIC_DECIMATOR_FILTER_REG 0x0D3E +#define DIGITAL_MUXES_REG1 0x0D3F +#define DIGITAL_MUXES_REG2 0x0D40 +#define AD1_DIGITAL_GAIN_REG 0x0D41 +#define AD2_DIGITAL_GAIN_REG 0x0D42 +#define AD3_DIGITAL_GAIN_REG 0x0D43 +#define AD4_DIGITAL_GAIN_REG 0x0D44 +#define AD5_DIGITAL_GAIN_REG 0x0D45 +#define AD6_DIGITAL_GAIN_REG 0x0D46 +#define DA1_DIGITAL_GAIN_REG 0x0D47 +#define DA2_DIGITAL_GAIN_REG 0x0D48 +#define DA3_DIGITAL_GAIN_REG 0x0D49 +#define DA4_DIGITAL_GAIN_REG 0x0D4A +#define DA5_DIGITAL_GAIN_REG 0x0D4B +#define DA6_DIGITAL_GAIN_REG 0x0D4C +#define AD1_TO_HFL_DIGITAL_GAIN_REG 0x0D4D +#define AD2_TO_HFR_DIGITAL_GAIN_REG 0x0D4E +#define HSL_EAR_DIGITAL_GAIN_REG 0x0D4F +#define HSR_DIGITAL_GAIN_REG 0x0D50 +#define SIDETONE_FIR1_GAIN_REG 0x0D51 +#define SIDETONE_FIR2_GAIN_REG 0x0D52 +#define ANC_FILTER_CONTROL_REG 0x0D53 +#define ANC_WARPED_GAIN_REG 0x0D54 +#define ANC_FIR_OUTPUT_GAIN_REG 0x0D55 +#define ANC_IIR_OUTPUT_GAIN_REG 0x0D56 +#define ANC_FIR_COEFF_MSB_REG 0x0D57 +#define ANC_FIR_COEFF_LSB_REG 0x0D58 +#define ANC_IIR_COEFF_MSB_REG 0x0D59 +#define ANC_IIR_COEFF_LSB_REG 0x0D5A +#define ANC_WARP_DELAY_MSB_REG 0x0D5B +#define ANC_WARP_DELAY_LSB_REG 0x0D5C +#define ANC_FIR_PEAK_MSB_REG 0x0D5D +#define ANC_FIR_PEAK_LSB_REG 0x0D5E +#define ANC_IIR_PEAK_MSB_REG 0x0D5F +#define ANC_IIR_PEAK_LSB_REG 0x0D60 +#define SIDETONE_FIR_ADDR_REG 0x0D61 +#define SIDETONE_FIR_COEFF_MSB_REG 0x0D62 +#define SIDETONE_FIR_COEFF_LSB_REG 0x0D63 +#define FILTERS_CONTROL_REG 0x0D64 +#define IRQ_MASK_LSB_REG 0x0D65 +#define IRQ_STATUS_LSB_REG 0x0D66 +#define IRQ_MASK_MSB_REG 0x0D67 +#define IRQ_STATUS_MSB_REG 0x0D68 +#define BURST_FIFO_INT_CONTROL_REG 0x0D69 +#define BURST_FIFO_LENGTH_REG 0x0D6A +#define BURST_FIFO_CONTROL_REG 0x0D6B +#define BURST_FIFO_SWITCH_FRAME_REG 0x0D6C +#define BURST_FIFO_WAKE_UP_DELAY_REG 0x0D6D +#define BURST_FIFO_SAMPLES_REG 0x0D6E +#define REVISION_REG 0x0D6F + +/* POWER_UP_CONTROL_REG Masks */ +#define DEVICE_POWER_UP 0x80 +#define ANALOG_PARTS_POWER_UP 0x08 + +/* SOFTWARE_RESET_REG Masks */ +#define SW_RESET 0x80 + +/* DIGITAL_AD_CHANNELS_ENABLE_REG Masks */ +#define EN_AD1 0x80 +#define EN_AD2 0x80 +#define EN_AD3 0x20 +#define EN_AD4 0x20 +#define EN_AD5 0x08 +#define EN_AD6 0x04 + +/* DIGITAL_DA_CHANNELS_ENABLE_REG Masks */ +#define EN_DA1 0x80 +#define EN_DA2 0x40 +#define EN_DA3 0x20 +#define EN_DA4 0x10 +#define EN_DA5 0x08 +#define EN_DA6 0x04 + +/* LOW_POWER_HS_EAR_CONF_REG Masks */ +#define LOW_POWER_HS 0x80 +#define HS_DAC_DRIVER_LP 0x40 +#define HS_DAC_LP 0x20 +#define EAR_DAC_LP 0x10 + +/* LINE_IN_MIC_CONF_REG Masks */ +#define EN_MIC1 0x80 +#define EN_MIC2 0x40 +#define EN_LIN_IN_L 0x20 +#define EN_LIN_IN_R 0x10 +#define MUT_MIC1 0x08 +#define MUT_MIC2 0x04 +#define MUT_LIN_IN_L 0x02 +#define MUT_LIN_IN_R 0x01 + +/* DMIC_ENABLE_REG Masks */ +#define EN_DMIC1 0x80 +#define EN_DMIC2 0x40 +#define EN_DMIC3 0x20 +#define EN_DMIC4 0x10 +#define EN_DMIC5 0x08 +#define EN_DMIC6 0x04 + +/* ADC_DAC_ENABLE_REG Masks */ +#define SEL_MIC1B_CLR_MIC1A 0x80 +#define SEL_LINR_CLR_MIC2 0x40 +#define POWER_UP_HSL_DAC 0x20 +#define POWER_UP_HSR_DAC 0x10 +#define POWER_UP_ADC1 0x04 +#define POWER_UP_ADC3 0x02 +#define POWER_UP_ADC2 0x01 + +/* ANALOG_OUTPUT_ENABLE_REG and DIGITAL_OUTPUT_ENABLE_REG and + MUTE_HS_EAR_REG Masks */ +#define EN_EAR_DAC_MASK 0x04 +#define EN_HSL_DAC_MASK 0x02 +#define EN_HSR_DAC_MASK 0x01 +#define EN_EAR_MASK 0x40 +#define EN_HSL_MASK 0x20 +#define EN_HSR_MASK 0x10 +#define EN_HFL_MASK 0x08 +#define EN_HFR_MASK 0x04 +#define EN_VIBL_MASK 0x02 +#define EN_VIBR_MASK 0x01 + +/* SHORT_CIRCUIT_DISABLE_REG Masks */ +#define HS_SHORT_DIS 0x20 +#define HS_PULL_DOWN_EN 0x10 +#define HS_OSC_EN 0x04 +#define DIS_HS_FAD 0x02 +#define HS_ZCD_DIS 0x01 + +/* NCP_ENABLE_HS_AUTOSTART_REG Masks */ +#define EN_NEG_CP 0x80 +#define HS_AUTO_EN 0x01 + +/* ANALOG_MIC1_GAIN_REG and ANALOG_MIC1_GAIN_REG Masks */ +#define MIC_ANALOG_GAIN_MASK 0x1F + +/*ANALOG_HS_GAIN_REG and ANALOG_LINE_IN_GAIN_REG Masks*/ +#define L_ANALOG_GAIN_MASK 0xF0 +#define R_ANALOG_GAIN_MASK 0x0F + +/* IF0_IF1_MASTER_CONF_REG Masks */ +#define EN_MASTGEN 0x80 +#define BITCLK_OSR_N_64 0x02 +#define BITCLK_OSR_N_128 0x04 +#define BITCLK_OSR_N_256 0x06 +#define EN_FSYNC_BITCLK 0x01 +#define EN_FSYNC_BITCLK1 0x10 + +/* IF0_CONF_REG and IF1_CONF_REG Masks */ +#define FSYNC_FALLING_EDGE 0x40 +#define BITCLK_FALLING_EDGE 0x20 +#define IF_DELAYED 0x10 +#define I2S_LEFT_ALIGNED_FORMAT 0x08 +#define TDM_FORMAT 0x04 +#define WORD_LENGTH_32 0x03 +#define WORD_LENGTH_24 0x02 +#define WORD_LENGTH_20 0x01 +#define WORD_LENGTH_16 0x00 + +/* TDM_IF_BYPASS_B_FIFO_REG Masks */ +#define IF0_BFifoEn 0x01 +#define IF0_MASTER 0x02 + +#define IF1_MASTER 0x20 +/* + * AD_ALLOCATION_TO_SLOT0_1_REG and AD_ALLOCATION_TO_SLOT2_3_REG and + * AD_ALLOCATION_TO_SLOT4_5_REG and AD_ALLOCATION_TO_SLOT6_7_REG Masks + */ +#define DATA_FROM_AD_OUT1 0x00 +#define DATA_FROM_AD_OUT2 0x01 +#define DATA_FROM_AD_OUT3 0x02 +#define DATA_FROM_AD_OUT4 0x03 +#define DATA_FROM_AD_OUT5 0x04 +#define DATA_FROM_AD_OUT6 0x05 +#define DATA_FROM_AD_OUT7 0x06 +#define DATA_FROM_AD_OUT8 0x07 +#define TRISTATE 0x0C + +/* + * SLOT_SELECTION_TO_DA1_REG and SLOT_SELECTION_TO_DA2_REG and + * SLOT_SELECTION_TO_DA3_REG and SLOT_SELECTION_TO_DA4_REG Masks + * SLOT_SELECTION_TO_DA5_REG and SLOT_SELECTION_TO_DA6_REG Masks + */ +#define SLOT08_FOR_DA_PATH 0x08 +#define SLOT09_FOR_DA_PATH 0x09 +#define SLOT10_FOR_DA_PATH 0x0A +#define SLOT11_FOR_DA_PATH 0x0B +#define SLOT12_FOR_DA_PATH 0x0C +#define SLOT13_FOR_DA_PATH 0x0D +#define SLOT14_FOR_DA_PATH 0x0E +#define SLOT15_FOR_DA_PATH 0x0F + +/* DIGITAL_MUXES_REG1 Masks */ +#define DA1_TO_HSL 0x80 +#define DA2_TO_HSR 0x40 +#define SEL_DMIC1_FOR_AD_OUT1 0x20 +#define SEL_DMIC2_FOR_AD_OUT2 0x10 +#define SEL_DMIC3_FOR_AD_OUT3 0x08 +/*#define SEL_DMIC5_FOR_AD_OUT5 0x04*/ +/*#define SEL_DMIC6_FOR_AD_OUT6 0x02*/ +/*#define SEL_DMIC1_FOR_AD_OUT1 0x01*/ + +/* + * AD1_DIGITAL_GAIN_REG and AD2_DIGITAL_GAIN_REG & AD3_DIGITAL_GAIN_REG Masks + * AD4_DIGITAL_GAIN_REG and AD5_DIGITAL_GAIN_REG & AD6_DIGITAL_GAIN_REG Masks + * DA1_DIGITAL_GAIN_REG and DA2_DIGITAL_GAIN_REG & DA3_DIGITAL_GAIN_REG Masks + * DA4_DIGITAL_GAIN_REG and DA5_DIGITAL_GAIN_REG & DA6_DIGITAL_GAIN_REG Masks + */ +#define DIS_FADING 0x40 +#define DIGITAL_GAIN_MASK 0x3F + +/* + * HSL_EAR_DIGITAL_GAIN_REG and HSR_DIGITAL_GAIN_REG Masks + */ +#define FADE_SPEED_MASK 0xC0 +#define DIS_DIG_GAIN_FADING 0x10 +#define HS_DIGITAL_GAIN_MASK 0x0F + +/* FMRx/FMTx Masks */ +#define SLOT24_FOR_DA_PATH 0x18 +#define SEL_AD_OUT8_FROM_DAIN7 0x20 +#define SLOT25_FOR_DA_PATH 0x19 +#define SEL_AD_OUT6_FROM_DAIN8 0x20 +#define SEL_IF8_FROM_AD_OUT7 0x60 +#define SEL_IF17_FROM_AD_OUT7 0x60 +#define SEL_IF16_FROM_AD_OUT8 0x07 + +#define SEL_IF6_FROM_AD_OUT5 0x04 +#define SEL_IF7_FROM_AD_OUT6 0x50 +#define SEL_IF17_FROM_AD_OUT6 0x50 +#define SEL_AD_OUT5_FROM_DAIN7 0x20 + +/* Burst FIFO Control Masks */ +#define WAKEUP_SIGNAL_SAMPLE_COUNT 0x1B +#define BURST_FIFO_TRANSFER_LENGTH 0xC0 +#define BURST_FIFO_INF_RUNNING 0x01 +#define BURST_FIFO_INF_IN_MASTER_MODE 0x02 +#define PRE_BIT_CLK0_COUNT 0x1C +#define BURST_FIFO_WAKUP_DEALAY 0x70 + +/* Filter Control Masks */ +/* SideTone Masks */ +#define SIDETONE_DIGITAL_GAIN_MASK 0x1F +#define FIR1_FROMAD1 0x0C +#define FIR1_FROMAD2 0x03 +#define FIR1_FROMAD3 0x08 +#define FIR1_DAIN1 0x0C + +#define FIR2_FROMAD2 0x00 +#define FIR2_FROMAD3 0x01 +#define FIR2_FROMAD4 0x02 +#define FIR2_DAIN2 0x03 + +#define FIR2_ANDFIR1AD3 0x09 +#define FIR_FILTERCONTROL 0x04 +#define APPLY_FIR_COEFFS_MASK 0x80 + +/* IRQ status masks */ +#define NCP_READY_MASK 0x80 + +/* AB8500 power control Masks */ +#define AB8500_VER_1_0 0x10 +#define AB8500_VER_1_1 0x11 +#define CLK_32K_OUT2_DISABLE 0x01 +#define INACTIVE_RESET_AUDIO 0x02 +#define AB8500_REQ_SYS_CLK 0x08 +#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10 +#define ENABLE_VINTCORE12_SUPPLY 0x04 +#define VAMIC2_ENABLE 0x10 +#define VAMIC1_ENABLE 0x08 +#define VDMIC_ENABLE 0x04 +#define VAUDIO_ENABLE 0x02 +#define GPIO27_DIR_OUTPUT 0x04 +#define GPIO29_DIR_OUTPUT 0x10 +#define GPIO31_DIR_OUTPUT 0x40 +#define GPIO35_DIR_OUTPUT 0X04 +#endif diff --git a/drivers/misc/audio_io_dev/ste_audio_io_core.c b/drivers/misc/audio_io_dev/ste_audio_io_core.c new file mode 100644 index 00000000000..129cd041e24 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_core.c @@ -0,0 +1,1464 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <mach/ste_audio_io_vibrator.h> +#include <mach/ste_audio.h> + +#include "ste_audio_io_core.h" +#include "ste_audio_io_hwctrl_common.h" +#include "ste_audio_io_ab8500_reg_defs.h" + +static struct audiocodec_context_t *ptr_audio_codec_cnxt; + +static struct clk *clk_ptr_msp1; +static struct clk *clk_ptr_msp3; +static struct clk *clk_ptr_sysclk; + +static struct regulator *regulator_vdmic; +static struct regulator *regulator_vaudio; +static struct regulator *regulator_vamic1; +static struct regulator *regulator_vamic2; +struct regulator *regulator_avsource; +static void ste_audio_io_init_transducer_cnxt(void); + +bool ste_audio_io_core_is_ready_for_suspend() +{ + bool err = false; + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + if ((!ptr_audio_codec_cnxt->power_client) && + (!ptr_audio_codec_cnxt->audio_codec_powerup)) + err = true; + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return err; +} +int ste_audio_io_core_api_init_data(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev); + int status = 0; + ptr_audio_codec_cnxt = kmalloc(sizeof(struct audiocodec_context_t), + GFP_KERNEL); + if (!ptr_audio_codec_cnxt) + return -ENOMEM; + + memset(ptr_audio_codec_cnxt, 0, sizeof(*ptr_audio_codec_cnxt)); + ptr_audio_codec_cnxt->dev = &pdev->dev; + mutex_init(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (pdata) { + if (pdata->audio) { + ptr_audio_codec_cnxt->gpio_altf_init = + pdata->audio->ste_gpio_altf_init; + ptr_audio_codec_cnxt->gpio_altf_exit = + pdata->audio->ste_gpio_altf_exit; + } + } + + regulator_vdmic = regulator_get(NULL, "v-dmic"); + if (IS_ERR(regulator_vdmic)) { + status = PTR_ERR(regulator_vdmic); + dev_err(ptr_audio_codec_cnxt->dev, + "Register error for v-dmic=%d", status); + goto free_audio_codec_cnxt; + } + regulator_vamic1 = regulator_get(NULL, "v-amic1"); + if (IS_ERR(regulator_vamic1)) { + status = PTR_ERR(regulator_vamic1); + dev_err(ptr_audio_codec_cnxt->dev, + "Register error for v-amic1=%d", status); + goto free_regulator_vdmic; + } + regulator_vamic2 = regulator_get(NULL, "v-amic2"); + if (IS_ERR(regulator_vamic2)) { + status = PTR_ERR(regulator_vamic2); + dev_err(ptr_audio_codec_cnxt->dev, + "Register error for v-amic2=%d", status); + goto free_regulator_vdmic_vamic1; + } + regulator_vaudio = regulator_get(NULL, "v-audio"); + if (IS_ERR(regulator_vaudio)) { + status = PTR_ERR(regulator_vaudio); + dev_err(ptr_audio_codec_cnxt->dev, + "Register error for v-audio=%d", status); + goto free_regulator_vdmic_vamic1_vamic2; + } + regulator_avsource = regulator_get(ptr_audio_codec_cnxt->dev, + "vcc-N2158"); + if (IS_ERR(regulator_avsource)) { + status = PTR_ERR(regulator_avsource); + dev_err(ptr_audio_codec_cnxt->dev, + "Register error for vcc-N2158=%d", status); + goto free_regulator_vdmic_vamic1_vamic2_vaudio; + } + + ste_audio_io_init_transducer_cnxt(); + return 0; + +free_regulator_vdmic_vamic1_vamic2_vaudio: + regulator_put(regulator_vaudio); +free_regulator_vdmic_vamic1_vamic2: + regulator_put(regulator_vamic2); +free_regulator_vdmic_vamic1: + regulator_put(regulator_vamic1); +free_regulator_vdmic: + regulator_put(regulator_vdmic); +free_audio_codec_cnxt: + kfree(ptr_audio_codec_cnxt); + return status; +} + +static struct transducer_context_t transducer_headset = { + .pwr_up_func = ste_audio_io_power_up_headset, + .pwr_down_func = ste_audio_io_power_down_headset, + .set_gain_func = ste_audio_io_set_headset_gain, + .get_gain_func = ste_audio_io_get_headset_gain, + .mute_func = ste_audio_io_mute_headset, + .unmute_func = ste_audio_io_unmute_headset, + .enable_fade_func = ste_audio_io_enable_fade_headset, + .disable_fade_func = ste_audio_io_disable_fade_headset, + .switch_to_burst_func = ste_audio_io_switch_to_burst_mode_headset, + .switch_to_normal_func = ste_audio_io_switch_to_normal_mode_headset +}; + +static struct transducer_context_t transducer_earpiece = { + .pwr_up_func = ste_audio_io_power_up_earpiece, + .pwr_down_func = ste_audio_io_power_down_earpiece, + .set_gain_func = ste_audio_io_set_earpiece_gain, + .get_gain_func = ste_audio_io_get_earpiece_gain, + .mute_func = ste_audio_io_mute_earpiece, + .unmute_func = ste_audio_io_unmute_earpiece, + .enable_fade_func = ste_audio_io_enable_fade_earpiece, + .disable_fade_func = ste_audio_io_disable_fade_earpiece +}; + +static struct transducer_context_t transducer_ihf = { + .pwr_up_func = ste_audio_io_power_up_ihf, + .pwr_down_func = ste_audio_io_power_down_ihf, + .set_gain_func = ste_audio_io_set_ihf_gain, + .get_gain_func = ste_audio_io_get_ihf_gain, + .mute_func = ste_audio_io_mute_ihf, + .unmute_func = ste_audio_io_unmute_ihf, + .enable_fade_func = ste_audio_io_enable_fade_ihf, + .disable_fade_func = ste_audio_io_disable_fade_ihf + +}; + +static struct transducer_context_t transducer_vibl = { + .pwr_up_func = ste_audio_io_power_up_vibl, + .pwr_down_func = ste_audio_io_power_down_vibl, + .set_gain_func = ste_audio_io_set_vibl_gain, + .get_gain_func = ste_audio_io_get_vibl_gain, + .mute_func = ste_audio_io_mute_vibl, + .unmute_func = ste_audio_io_unmute_vibl, + .enable_fade_func = ste_audio_io_enable_fade_vibl, + .disable_fade_func = ste_audio_io_disable_fade_vibl +}; + +static struct transducer_context_t transducer_vibr = { + .pwr_up_func = ste_audio_io_power_up_vibr, + .pwr_down_func = ste_audio_io_power_down_vibr, + .set_gain_func = ste_audio_io_set_vibr_gain, + .get_gain_func = ste_audio_io_get_vibr_gain, + .mute_func = ste_audio_io_mute_vibr, + .unmute_func = ste_audio_io_unmute_vibr, + .enable_fade_func = ste_audio_io_enable_fade_vibr, + .disable_fade_func = ste_audio_io_disable_fade_vibr +}; + +static struct transducer_context_t transducer_mic1a = { + .pwr_up_func = ste_audio_io_power_up_mic1a, + .pwr_down_func = ste_audio_io_power_down_mic1a, + .set_gain_func = ste_audio_io_set_mic1a_gain, + .get_gain_func = ste_audio_io_get_mic1a_gain, + .mute_func = ste_audio_io_mute_mic1a, + .unmute_func = ste_audio_io_unmute_mic1a, + .enable_fade_func = ste_audio_io_enable_fade_mic1a, + .disable_fade_func = ste_audio_io_disable_fade_mic1a +}; + +static struct transducer_context_t transducer_mic1b = { + .pwr_up_func = ste_audio_io_power_up_mic1b, + .pwr_down_func = ste_audio_io_power_down_mic1b, + .set_gain_func = ste_audio_io_set_mic1a_gain, + .get_gain_func = ste_audio_io_get_mic1a_gain, + .mute_func = ste_audio_io_mute_mic1a, + .unmute_func = ste_audio_io_unmute_mic1a, + .enable_fade_func = ste_audio_io_enable_fade_mic1a, + .disable_fade_func = ste_audio_io_disable_fade_mic1a, + .enable_loop = ste_audio_io_enable_loop_mic1b, + .disable_loop = ste_audio_io_disable_loop_mic1b +}; + +static struct transducer_context_t transducer_mic2 = { + .pwr_up_func = ste_audio_io_power_up_mic2, + .pwr_down_func = ste_audio_io_power_down_mic2, + .set_gain_func = ste_audio_io_set_mic2_gain, + .get_gain_func = ste_audio_io_get_mic2_gain, + .mute_func = ste_audio_io_mute_mic2, + .unmute_func = ste_audio_io_unmute_mic2, + .enable_fade_func = ste_audio_io_enable_fade_mic2, + .disable_fade_func = ste_audio_io_disable_fade_mic2 +}; + +static struct transducer_context_t transducer_lin = { + .pwr_up_func = ste_audio_io_power_up_lin, + .pwr_down_func = ste_audio_io_power_down_lin, + .set_gain_func = ste_audio_io_set_lin_gain, + .get_gain_func = ste_audio_io_get_lin_gain, + .mute_func = ste_audio_io_mute_lin, + .unmute_func = ste_audio_io_unmute_lin, + .enable_fade_func = ste_audio_io_enable_fade_lin, + .disable_fade_func = ste_audio_io_disable_fade_lin +}; + +static struct transducer_context_t transducer_dmic12 = { + .pwr_up_func = ste_audio_io_power_up_dmic12, + .pwr_down_func = ste_audio_io_power_down_dmic12, + .set_gain_func = ste_audio_io_set_dmic12_gain, + .get_gain_func = ste_audio_io_get_dmic12_gain, + .mute_func = ste_audio_io_mute_dmic12, + .unmute_func = ste_audio_io_unmute_dmic12, + .enable_fade_func = ste_audio_io_enable_fade_dmic12, + .disable_fade_func = ste_audio_io_disable_fade_dmic12, + .enable_loop = ste_audio_io_enable_loop_dmic12, + .disable_loop = ste_audio_io_disable_loop_dmic12 +}; + +static struct transducer_context_t transducer_dmic34 = { + .pwr_up_func = ste_audio_io_power_up_dmic34, + .pwr_down_func = ste_audio_io_power_down_dmic34, + .set_gain_func = ste_audio_io_set_dmic34_gain, + .get_gain_func = ste_audio_io_get_dmic34_gain, + .mute_func = ste_audio_io_mute_dmic34, + .unmute_func = ste_audio_io_unmute_dmic34, + .enable_fade_func = ste_audio_io_enable_fade_dmic34, + .disable_fade_func = ste_audio_io_disable_fade_dmic34 +}; + +static struct transducer_context_t transducer_dmic56 = { + .pwr_up_func = ste_audio_io_power_up_dmic56, + .pwr_down_func = ste_audio_io_power_down_dmic56, + .set_gain_func = ste_audio_io_set_dmic56_gain, + .get_gain_func = ste_audio_io_get_dmic56_gain, + .mute_func = ste_audio_io_mute_dmic56, + .unmute_func = ste_audio_io_unmute_dmic56, + .enable_fade_func = ste_audio_io_enable_fade_dmic56, + .disable_fade_func = ste_audio_io_disable_fade_dmic56, +}; + +static struct transducer_context_t transducer_fmrx = { + .pwr_up_func = ste_audio_io_power_up_fmrx, + .pwr_down_func = ste_audio_io_power_down_fmrx, +}; + +static struct transducer_context_t transducer_fmtx = { + .pwr_up_func = ste_audio_io_power_up_fmtx, + .pwr_down_func = ste_audio_io_power_down_fmtx, +}; + +static struct transducer_context_t transducer_bluetooth = { + .pwr_up_func = ste_audio_io_power_up_bluetooth, + .pwr_down_func = ste_audio_io_power_down_bluetooth, +}; + +static void ste_audio_io_init_transducer_cnxt(void) +{ + ptr_audio_codec_cnxt->transducer[HS_CH] = &transducer_headset; + ptr_audio_codec_cnxt->transducer[EAR_CH] = &transducer_earpiece; + ptr_audio_codec_cnxt->transducer[IHF_CH] = &transducer_ihf; + ptr_audio_codec_cnxt->transducer[VIBL_CH] = &transducer_vibl; + ptr_audio_codec_cnxt->transducer[VIBR_CH] = &transducer_vibr; + ptr_audio_codec_cnxt->transducer[MIC1A_CH] = &transducer_mic1a; + ptr_audio_codec_cnxt->transducer[MIC1B_CH] = &transducer_mic1b; + ptr_audio_codec_cnxt->transducer[MIC2_CH] = &transducer_mic2; + ptr_audio_codec_cnxt->transducer[LIN_CH] = &transducer_lin; + ptr_audio_codec_cnxt->transducer[DMIC12_CH] = &transducer_dmic12; + ptr_audio_codec_cnxt->transducer[DMIC34_CH] = &transducer_dmic34; + ptr_audio_codec_cnxt->transducer[DMIC56_CH] = &transducer_dmic56; + ptr_audio_codec_cnxt->transducer[FMRX_CH] = &transducer_fmrx; + ptr_audio_codec_cnxt->transducer[FMTX_CH] = &transducer_fmtx; + ptr_audio_codec_cnxt->transducer[BLUETOOTH_CH] = &transducer_bluetooth; +} + +void ste_audio_io_core_api_free_data(void) +{ + regulator_put(regulator_vdmic); + regulator_put(regulator_vamic1); + regulator_put(regulator_vamic2); + regulator_put(regulator_vaudio); + regulator_put(regulator_avsource); + kfree(ptr_audio_codec_cnxt); +} + +static int ste_audio_io_core_api_enable_regulators(int channel_type) +{ + int error = 0; + + switch (channel_type) { + case EAR_CH: + case HS_CH: + case IHF_CH: + case VIBL_CH: + case VIBR_CH: + case LIN_CH: + case FMRX_CH: + case FMTX_CH: + case BLUETOOTH_CH: + /* vaduio already enabled + no additional regualtor required */ + break; + + case MIC1A_CH: + case MIC1B_CH: + error = regulator_enable(regulator_vamic1); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to enable regulator vamic1 error = %d", error); + break; + + case MIC2_CH: + error = regulator_enable(regulator_vamic2); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to enable regulator vamic2 error = %d", error); + break; + + case DMIC12_CH: + case DMIC34_CH: + case DMIC56_CH: + case MULTI_MIC_CH: + error = regulator_enable(regulator_vdmic); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to enable regulator vdmic error = %d", error); + } + return error; +} + +static int ste_audio_io_core_api_disable_regulators(int channel_type) +{ + int error = 0; + + switch (channel_type) { + case EAR_CH: + case HS_CH: + case IHF_CH: + case VIBL_CH: + case VIBR_CH: + case LIN_CH: + case FMRX_CH: + case FMTX_CH: + case BLUETOOTH_CH: + /* no need to disable separately*/ + break; + + case MIC1A_CH: + case MIC1B_CH: + error = regulator_disable(regulator_vamic1); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to disable regulator vamic1 error = %d", error); + break; + + case MIC2_CH: + error = regulator_disable(regulator_vamic2); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to disable regulator vamic2 error = %d", error); + break; + + case DMIC12_CH: + case DMIC34_CH: + case DMIC56_CH: + case MULTI_MIC_CH: + error = regulator_disable(regulator_vdmic); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "unable to disable regulator vdmic error = %d", error); + } + return error; +} + +int ste_audio_io_core_api_powerup_audiocodec(int power_client) +{ + int error = 0; + int acodec_device_id; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + acodec_device_id = abx500_get_chip_id(&ste_audio_io_device->dev); + + /* + * If there is no power client registered, power up + * common audio blocks for audio and vibrator + */ + if (!ptr_audio_codec_cnxt->power_client) { + __u8 data, old_data; + + old_data = HW_REG_READ(AB8500_CTRL3_REG); + + /* Enable 32 Khz clock signal on Clk32KOut2 ball */ + data = (~CLK_32K_OUT2_DISABLE) & old_data; + error = HW_REG_WRITE(AB8500_CTRL3_REG, data); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "enabling 32KHz clock error = %d", error); + goto err_cleanup; + } + data = INACTIVE_RESET_AUDIO | old_data; + error = HW_REG_WRITE(AB8500_CTRL3_REG, data); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "deactivate audio codec reset error = %d", error); + goto err_cleanup; + } + old_data = HW_REG_READ(AB8500_SYSULPCLK_CTRL1_REG); + data = ENABLE_AUDIO_CLK_TO_AUDIO_BLK | old_data; + + error = HW_REG_WRITE(AB8500_SYSULPCLK_CTRL1_REG, data); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "enabling clock to audio block error = %d", error); + goto err_cleanup; + } + regulator_enable(regulator_vaudio); + + old_data = HW_REG_READ(AB8500_GPIO_DIR4_REG); + data = (GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | + GPIO31_DIR_OUTPUT) | old_data; + error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, data); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "setting gpio dir4 error = %d", error); + goto err_cleanup; + } + error = HW_REG_WRITE(SOFTWARE_RESET_REG, SW_RESET); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "Software reset error=%d", error); + goto err_cleanup; + } + + error = HW_ACODEC_MODIFY_WRITE(POWER_UP_CONTROL_REG, + (DEVICE_POWER_UP|ANALOG_PARTS_POWER_UP), 0); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "Device Power Up, error=%d", error); + goto err_cleanup; + } + } + /* Save information that given client already powered up audio block */ + ptr_audio_codec_cnxt->power_client |= power_client; + + /* If audio block requested power up, turn on additional audio blocks */ + if (power_client == STE_AUDIOIO_POWER_AUDIO) { + if (!ptr_audio_codec_cnxt->audio_codec_powerup) { + clk_ptr_sysclk = + clk_get(ptr_audio_codec_cnxt->dev, "sysclk"); + if (!IS_ERR(clk_ptr_sysclk)) { + error = clk_enable(clk_ptr_sysclk); + if (error) + goto err_cleanup; + } else { + error = -EFAULT; + goto err_cleanup; + } + + clk_ptr_msp1 = clk_get_sys("msp1", NULL); + if (!IS_ERR(clk_ptr_msp1)) { + error = clk_enable(clk_ptr_msp1); + if (error) + goto err_cleanup; + } else { + error = -EFAULT; + goto err_cleanup; + } + + if (AB8500_REV_20 == acodec_device_id) { + clk_ptr_msp3 = clk_get_sys("msp3", NULL); + if (!IS_ERR(clk_ptr_msp3)) { + error = clk_enable(clk_ptr_msp3); + if (error) + goto err_cleanup; + } else { + error = -EFAULT; + goto err_cleanup; + } + } + + if (ptr_audio_codec_cnxt->gpio_altf_init) { + error = ptr_audio_codec_cnxt->gpio_altf_init(); + if (error) + goto err_cleanup; + } + + error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, + EN_MASTGEN, 0); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "Enable Master Generator, error=%d", error); + goto err_cleanup; + } + + error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, + IF0_MASTER, 0); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "IF0: Master Mode, error=%d", error); + goto err_cleanup; + } + + /* Configuring IF0 */ + + error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, + BITCLK_OSR_N_256, 0); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "IF0: Enable FsBitClk & FSync error=%d", error); + goto err_cleanup; + } + + error = HW_REG_WRITE(IF0_CONF_REG, IF_DELAYED + | TDM_FORMAT | WORD_LENGTH_20); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "IF0: TDM Format 16 Bits word length, error=%d", + error); + goto err_cleanup; + } + } + ptr_audio_codec_cnxt->audio_codec_powerup++; + } +err_cleanup: + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return error; +} + +int ste_audio_io_core_api_powerdown_audiocodec(int power_client) +{ + int error = 0; + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + /* Update power client status */ + if (power_client == STE_AUDIOIO_POWER_AUDIO) { + ptr_audio_codec_cnxt->audio_codec_powerup--; + if (!ptr_audio_codec_cnxt->audio_codec_powerup) { + ptr_audio_codec_cnxt->power_client &= ~power_client; + clk_disable(clk_ptr_sysclk); + clk_put(clk_ptr_sysclk); + clk_disable(clk_ptr_msp1); + clk_put(clk_ptr_msp1); + if (AB8500_REV_20 == + abx500_get_chip_id(&ste_audio_io_device->dev)) { + clk_disable(clk_ptr_msp3); + clk_put(clk_ptr_msp3); + } + + if (ptr_audio_codec_cnxt->gpio_altf_exit) { + error = ptr_audio_codec_cnxt->gpio_altf_exit(); + if (error) + goto err_cleanup; + } + } + } else + ptr_audio_codec_cnxt->power_client &= ~power_client; + + /* If no power client registered, power down audio block */ + if (!ptr_audio_codec_cnxt->power_client) { + regulator_disable(regulator_vaudio); + if (error != 0) { + dev_err(ptr_audio_codec_cnxt->dev, + "Device Power Down and Analog Parts Power Down error = %d ", + error); + goto err_cleanup; + } + } + +err_cleanup: + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return error; +} +/** + * @brief Read from AB8500 device + * @dev_data Pointer to the structure __audioio_data + * @return 0 + */ + +int ste_audio_io_core_api_access_read(struct audioio_data_t *dev_data) +{ + int reg; + if (NULL == dev_data) + return -EFAULT; + reg = (dev_data->block<<8)|(dev_data->addr&0xff); + dev_data->data = HW_REG_READ(reg); + return 0; +} +/** + * @brief Write on AB8500 device + * @dev_data Pointer to the structure __audioio_data + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_core_api_access_write(struct audioio_data_t *dev_data) +{ + int retval, reg; + if (NULL == dev_data) + return -EFAULT; + + reg = (dev_data->block<<8)|(dev_data->addr&0xff); + retval = HW_REG_WRITE(reg, dev_data->data); + + return retval; +} +/** + * @brief Store the power and mute status of transducer + * @channel_index Channel-index of transducer + * @ptr Array storing the status + * @value status being stored + * @return 0 on success otherwise negative error code + */ + +void ste_audio_io_core_api_store_data(enum AUDIOIO_CH_INDEX channel_index, + int *ptr, int value) +{ + if (channel_index & e_CHANNEL_1) + ptr[0] = value; + + if (channel_index & e_CHANNEL_2) + ptr[1] = value; + + if (channel_index & e_CHANNEL_3) + ptr[2] = value; + + if (channel_index & e_CHANNEL_4) + ptr[3] = value; +} +/** + * @brief Get power or mute status on a specific channel + * @channel_index Channel-index of the transducer + * @ptr Pointer to is_power_up array or is_muted array + * @return status of control switch + */ +enum AUDIOIO_COMMON_SWITCH ste_audio_io_core_api_get_status( + enum AUDIOIO_CH_INDEX channel_index, int *ptr) +{ + if (channel_index & e_CHANNEL_1) { + if (AUDIOIO_TRUE == ptr[0]) + return AUDIOIO_COMMON_ON; + else + return AUDIOIO_COMMON_OFF; + } + + if (channel_index & e_CHANNEL_2) { + if (AUDIOIO_TRUE == ptr[1]) + return AUDIOIO_COMMON_ON; + else + return AUDIOIO_COMMON_OFF; + } + + if (channel_index & e_CHANNEL_3) { + if (AUDIOIO_TRUE == ptr[2]) + return AUDIOIO_COMMON_ON; + else + return AUDIOIO_COMMON_OFF; + } + + if (channel_index & e_CHANNEL_4) { + if (AUDIOIO_TRUE == ptr[3]) + return AUDIOIO_COMMON_ON; + else + return AUDIOIO_COMMON_OFF; + } + return 0; +} + +int ste_audio_io_core_api_acodec_power_control(struct audioio_acodec_pwr_ctrl_t + *audio_acodec_pwr_ctrl) +{ + int error = 0; + if (audio_acodec_pwr_ctrl->ctrl_switch == AUDIOIO_COMMON_ON) + error = ste_audio_io_core_api_powerup_audiocodec( + STE_AUDIOIO_POWER_AUDIO); + else + error = ste_audio_io_core_api_powerdown_audiocodec( + STE_AUDIOIO_POWER_AUDIO); + + return error; +} +/** + * @brief Control for powering on/off HW components on a specific channel + * @pwr_ctrl Pointer to the structure __audioio_pwr_ctrl + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_core_api_power_control_transducer( + struct audioio_pwr_ctrl_t *pwr_ctrl) +{ + int error = 0; + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + + channel_index = pwr_ctrl->channel_index; + + if ((pwr_ctrl->channel_type < FIRST_CH) + || (pwr_ctrl->channel_type > LAST_CH)) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[pwr_ctrl->channel_type]; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (AUDIOIO_COMMON_ON == pwr_ctrl->ctrl_switch) { + if (ptr->pwr_up_func) { + error = ste_audio_io_core_api_enable_regulators( + pwr_ctrl->channel_type); + if (error) + goto free_mutex; + + error = ptr->pwr_up_func(pwr_ctrl->channel_index, + ptr_audio_codec_cnxt->dev); + if (0 == error) { + ste_audio_io_core_api_store_data(channel_index, + ptr->is_power_up, AUDIOIO_TRUE); + } + } + } else { + if (ptr->pwr_down_func) { + error = ptr->pwr_down_func(pwr_ctrl->channel_index, + ptr_audio_codec_cnxt->dev); + if (0 == error) { + ste_audio_io_core_api_store_data(channel_index, + ptr->is_power_up, AUDIOIO_FALSE); + } + error = ste_audio_io_core_api_disable_regulators( + pwr_ctrl->channel_type); + } + } + +free_mutex: + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} +/** + * @brief Query power state of HW path on specified channel + * @pwr_ctrl Pointer to the structure __audioio_pwr_ctrl + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_core_api_power_status_transducer( + struct audioio_pwr_ctrl_t *pwr_ctrl) +{ + + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + + channel_index = pwr_ctrl->channel_index; + + if ((pwr_ctrl->channel_type < FIRST_CH) + || (pwr_ctrl->channel_type > LAST_CH)) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[pwr_ctrl->channel_type]; + + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + + pwr_ctrl->ctrl_switch = ste_audio_io_core_api_get_status(channel_index, + ptr->is_power_up); + + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return 0; + +} + +int ste_audio_io_core_api_loop_control(struct audioio_loop_ctrl_t *loop_ctrl) +{ + int error = 0; + struct transducer_context_t *ptr = NULL; + + if ((loop_ctrl->channel_type < FIRST_CH) + || (loop_ctrl->channel_type > LAST_CH)) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[loop_ctrl->channel_type]; + + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (AUDIOIO_COMMON_ON == loop_ctrl->ctrl_switch) { + if (ptr->enable_loop) { + error = ptr->enable_loop(loop_ctrl->channel_index, + loop_ctrl->hw_loop, + loop_ctrl->loop_gain, + ptr_audio_codec_cnxt->dev, + ptr_audio_codec_cnxt->transducer); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "Loop enable failed for hw loop = %d, error = %d ", + (int)loop_ctrl->hw_loop, error); + } else { + error = -EFAULT; + dev_err(ptr_audio_codec_cnxt->dev, + "Hw Loop enable does not exist for channel= %d, error = %d ", + (int)loop_ctrl->channel_type, error); + } + } else { + if (ptr->disable_loop) { + error = ptr->disable_loop(loop_ctrl->channel_index, + loop_ctrl->hw_loop, + ptr_audio_codec_cnxt->dev, + ptr_audio_codec_cnxt->transducer); + if (error) + dev_err(ptr_audio_codec_cnxt->dev, + "Loop disable failed for hw loop = %d, error = %d ", + (int)loop_ctrl->hw_loop, error); + } else { + error = -EFAULT; + dev_err(ptr_audio_codec_cnxt->dev, + "Hw Loop disable does not exist for channel= %d, error = %d ", + (int)loop_ctrl->channel_type, error); + } + } + + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} + +int ste_audio_io_core_api_loop_status(struct audioio_loop_ctrl_t *loop_ctrl) +{ + return 0; +} + +int ste_audio_io_core_api_get_transducer_gain_capability( + struct audioio_get_gain_t *get_gain) +{ + return 0; +} + +int ste_audio_io_core_api_gain_capabilities_loop( + struct audioio_gain_loop_t *gain_loop) +{ + if ((gain_loop->channel_type < FIRST_CH) + || (gain_loop->channel_type > LAST_CH)) + return -EINVAL; + + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + gain_loop->num_loop = + transducer_max_no_Of_supported_loops[gain_loop->channel_type]; + gain_loop->max_gains = max_no_of_loop_gains[gain_loop->channel_type]; + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return 0; +} + +int ste_audio_io_core_api_supported_loops( + struct audioio_support_loop_t *support_loop) +{ + if ((support_loop->channel_type < FIRST_CH) + || (support_loop->channel_type > LAST_CH)) + return -EINVAL; + + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + support_loop->spprtd_loop_index = + transducer_no_Of_supported_loop_indexes[support_loop->channel_type]; + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return 0; +} + +int ste_audio_io_core_api_gain_descriptor_transducer( + struct audioio_gain_desc_trnsdr_t *gdesc_trnsdr) +{ + return 0; +} +/** + * @brief Control for muting a specific channel in HW + * @mute_trnsdr Pointer to the structure __audioio_mute_trnsdr + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_core_api_mute_control_transducer( + struct audioio_mute_trnsdr_t *mute_trnsdr) +{ + int error = 0; + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + + channel_index = mute_trnsdr->channel_index; + + if ((mute_trnsdr->channel_type < FIRST_CH) + || (mute_trnsdr->channel_type > LAST_CH)) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[mute_trnsdr->channel_type]; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (AUDIOIO_COMMON_ON == mute_trnsdr->ctrl_switch) { + if (ptr->mute_func) { + error = ptr->mute_func(mute_trnsdr->channel_index, + ptr_audio_codec_cnxt->dev); + if (0 == error) { + ste_audio_io_core_api_store_data(channel_index , + ptr->is_muted, AUDIOIO_TRUE); + } + } + } else { + if (ptr->unmute_func) { + if (0 == ptr->unmute_func(channel_index, ptr->gain, + ptr_audio_codec_cnxt->dev)) { + ste_audio_io_core_api_store_data(channel_index, + ptr->is_muted, AUDIOIO_FALSE); + } + } + } + + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} +/** + * @brief Query state of mute on specified channel + * @mute_trnsdr Pointer to the structure __audioio_mute_trnsdr + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_core_api_mute_status_transducer( + struct audioio_mute_trnsdr_t *mute_trnsdr) +{ + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + + channel_index = mute_trnsdr->channel_index; + + if ((mute_trnsdr->channel_type < FIRST_CH) + || (mute_trnsdr->channel_type > LAST_CH)) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[mute_trnsdr->channel_type]; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + mute_trnsdr->ctrl_switch = ste_audio_io_core_api_get_status( + channel_index, ptr->is_muted); + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return 0; +} +/** + * @brief control the fading on the transducer called on. + * @fade_ctrl Pointer to the structure __audioio_fade_ctrl + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_core_api_fading_control(struct audioio_fade_ctrl_t *fade_ctrl) +{ + int error = 0; + struct transducer_context_t *ptr = NULL; + + if ((fade_ctrl->channel_type < FIRST_CH) + || (fade_ctrl->channel_type > LAST_CH)) + return -EINVAL; + ptr = ptr_audio_codec_cnxt->transducer[fade_ctrl->channel_type]; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (AUDIOIO_COMMON_ON == fade_ctrl->ctrl_switch) + error = ptr->enable_fade_func(ptr_audio_codec_cnxt->dev); + + else + error = ptr->disable_fade_func(ptr_audio_codec_cnxt->dev); + + + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} +/** + * @brief control the low power mode of headset. + * @burst_ctrl Pointer to the structure __audioio_burst_ctrl + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_core_api_burstmode_control( + struct audioio_burst_ctrl_t *burst_ctrl) +{ + int error = 0; + struct transducer_context_t *ptr = NULL; + int burst_fifo_switch_frame; + + burst_fifo_switch_frame = burst_ctrl->burst_fifo_switch_frame; + + if ((burst_ctrl->channel_type < FIRST_CH) + || (burst_ctrl->channel_type > LAST_CH)) + return -EINVAL; + ptr = ptr_audio_codec_cnxt->transducer[burst_ctrl->channel_type]; + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + if (AUDIOIO_COMMON_ON == burst_ctrl->ctrl_switch) { + if (ptr->switch_to_burst_func) + error = ptr->switch_to_burst_func( + burst_fifo_switch_frame, + ptr_audio_codec_cnxt->dev); + } else + if (ptr->switch_to_normal_func) + error = ptr->switch_to_normal_func( + ptr_audio_codec_cnxt->dev); + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} +/** + * @brief Convert channel index to array index + * @channel_index Channel Index of transducer + * @return Array index corresponding to the specified channel index + */ + +int convert_channel_index_to_array_index(enum AUDIOIO_CH_INDEX channel_index) +{ + if (channel_index & e_CHANNEL_1) + return 0; + else if (channel_index & e_CHANNEL_2) + return 1; + else if (channel_index & e_CHANNEL_3) + return 2; + else + return 3; +} + +/** + * @brief Set individual gain along the HW path of a specified channel + * @gctrl_trnsdr Pointer to the structure __audioio_gain_ctrl_trnsdr + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_core_api_gain_control_transducer( + struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr) +{ + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + int ch_array_index; + u16 gain_index; + int gain_value; + u32 linear; + int channel_type; + int error; + int min_gain, max_gain, gain; + + if ((gctrl_trnsdr->channel_type < FIRST_CH) + || (gctrl_trnsdr->channel_type > LAST_CH)) + return -EINVAL; + + if (gctrl_trnsdr->gain_index >= MAX_NO_GAINS) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[gctrl_trnsdr->channel_type]; + channel_index = gctrl_trnsdr->channel_index; + gain_index = gctrl_trnsdr->gain_index; + gain_value = gctrl_trnsdr->gain_value; + linear = gctrl_trnsdr->linear; + channel_type = gctrl_trnsdr->channel_type; + + ch_array_index = convert_channel_index_to_array_index(channel_index); + if (linear) { /* Gain is in the range 0 to 100 */ + min_gain = gain_descriptor[channel_type]\ + [ch_array_index][gain_index].min_gain; + max_gain = gain_descriptor[channel_type]\ + [ch_array_index][gain_index].max_gain; + + gain = ((gain_value * (max_gain - min_gain))/100) + min_gain; + } else + /* Convert to db */ + gain = gain_value/100; + + gain_value = gain; + +#if 1 + if (gain_index >= transducer_no_of_gains[channel_type]) + return -EINVAL; + + if (gain_value < gain_descriptor[channel_type]\ + [ch_array_index][gain_index].min_gain) + return -EINVAL; + + if (gain_value > gain_descriptor[channel_type]\ + [ch_array_index][gain_index].max_gain) + return -EINVAL; + +#endif + + /* aquire mutex */ + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + error = ptr->set_gain_func(channel_index, + gain_index, gain_value, linear, + ptr_audio_codec_cnxt->dev); + if (0 == error) + ste_audio_io_core_api_store_data(channel_index , + ptr->gain, gain_value); + + + /* release mutex */ + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + return error; +} +/** + * @brief Get individual gain along the HW path of a specified channel + * @gctrl_trnsdr Pointer to the structure __audioio_gain_ctrl_trnsdr + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_core_api_gain_query_transducer( + struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr) +{ + struct transducer_context_t *ptr = NULL; + enum AUDIOIO_CH_INDEX channel_index; + u16 gain_index; + u32 linear; + int left_volume, right_volume; + int max_gain, min_gain; + int ch_array_index; + + if ((gctrl_trnsdr->channel_type < FIRST_CH) + || (gctrl_trnsdr->channel_type > LAST_CH)) + return -EINVAL; + + if (gctrl_trnsdr->gain_index >= MAX_NO_GAINS) + return -EINVAL; + + ptr = ptr_audio_codec_cnxt->transducer[gctrl_trnsdr->channel_type]; + + channel_index = gctrl_trnsdr->channel_index; + gain_index = gctrl_trnsdr->gain_index; + linear = gctrl_trnsdr->linear; + + ptr->get_gain_func(&left_volume, &right_volume, gain_index, + ptr_audio_codec_cnxt->dev); + + ch_array_index = convert_channel_index_to_array_index(channel_index); + max_gain = gain_descriptor[gctrl_trnsdr->channel_type]\ + [ch_array_index][gain_index].max_gain; + min_gain = gain_descriptor[gctrl_trnsdr->channel_type]\ + [ch_array_index][gain_index].min_gain; + + switch (channel_index) { + case e_CHANNEL_1: + gctrl_trnsdr->gain_value = linear ? \ + min_gain+left_volume*(max_gain-min_gain)/100 : left_volume; + break; + case e_CHANNEL_2: + gctrl_trnsdr->gain_value = linear ? \ + min_gain+right_volume*(max_gain-min_gain)/100 : right_volume; + break; + case e_CHANNEL_3: + break; + case e_CHANNEL_4: + break; + case e_CHANNEL_ALL: + if (left_volume == right_volume) { + if (linear) + gctrl_trnsdr->gain_value = + min_gain+right_volume*(max_gain-min_gain)/100; + else + gctrl_trnsdr->gain_value = right_volume; + } + } + + return 0; +} + + +int ste_audio_io_core_api_fsbitclk_control( + struct audioio_fsbitclk_ctrl_t *fsbitclk_ctrl) +{ + int error = 0; + + if (AUDIOIO_COMMON_ON == fsbitclk_ctrl->ctrl_switch) + error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, + EN_FSYNC_BITCLK, 0); + else + error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, 0, + EN_FSYNC_BITCLK); + + return error; +} +int ste_audio_io_core_api_pseudoburst_control( + struct audioio_pseudoburst_ctrl_t *pseudoburst_ctrl) +{ + int error = 0; + + return error; +} +int ste_audio_io_core_debug(int x) +{ + debug_audioio(x); + +return 0; +} + +/** + * ste_audioio_vibrator_alloc() + * @client: Client id which allocates vibrator + * @mask: Mask against which vibrator usage is checked + * + * This function allocates vibrator. + * Mask is added here as audioio driver controls left and right vibrator + * separately (can work independently). In case when audioio has allocated + * one of its channels (left or right) it should be still able to allocate + * the other channel. + * + * Returns: + * 0 - Success + * -EBUSY - other client already registered + **/ +int ste_audioio_vibrator_alloc(int client, int mask) +{ + int error = 0; + + /* Check if other client is already using vibrator */ + if (ptr_audio_codec_cnxt->vibra_client & ~mask) + error = -EBUSY; + else + ptr_audio_codec_cnxt->vibra_client |= client; + + return error; +} + +/** + * ste_audioio_vibrator_release() + * @client: Client id which releases vibrator + * + * This function releases vibrator + **/ +void ste_audioio_vibrator_release(int client) +{ + ptr_audio_codec_cnxt->vibra_client &= ~client; +} + +/** + * ste_audioio_vibrator_pwm_control() + * @client: Client id which will use vibrator + * @left_speed: Left vibrator speed + * @right_speed: Right vibrator speed + * + * This function controls vibrator using PWM source + * + * Returns: + * 0 - success + * -EBUSY - Vibrator already used + **/ +int ste_audioio_vibrator_pwm_control( + int client, + struct ste_vibra_speed left_speed, + struct ste_vibra_speed right_speed) +{ + int error = 0; + + mutex_lock(&ptr_audio_codec_cnxt->audio_io_mutex); + + /* Try to allocate vibrator for given client */ + error = ste_audioio_vibrator_alloc(client, client); + + mutex_unlock(&ptr_audio_codec_cnxt->audio_io_mutex); + + if (error) + return error; + + /* Duty cycle supported by vibrator's PWM is 0-100 */ + if (left_speed.positive > STE_AUDIOIO_VIBRATOR_MAX_SPEED) + left_speed.positive = STE_AUDIOIO_VIBRATOR_MAX_SPEED; + + if (right_speed.positive > STE_AUDIOIO_VIBRATOR_MAX_SPEED) + right_speed.positive = STE_AUDIOIO_VIBRATOR_MAX_SPEED; + + if (left_speed.negative > STE_AUDIOIO_VIBRATOR_MAX_SPEED) + left_speed.negative = STE_AUDIOIO_VIBRATOR_MAX_SPEED; + + if (right_speed.negative > STE_AUDIOIO_VIBRATOR_MAX_SPEED) + right_speed.negative = STE_AUDIOIO_VIBRATOR_MAX_SPEED; + + if (left_speed.negative || right_speed.negative || + left_speed.positive || right_speed.positive) { + /* Power up audio block for vibrator */ + error = ste_audio_io_core_api_powerup_audiocodec( + STE_AUDIOIO_POWER_VIBRA); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Audio power up failed %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + (EN_VIBL_MASK|EN_VIBR_MASK), 0); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Powerup Vibrator Class-D driver %d", + error); + return error; + } + + error = HW_REG_WRITE(VIB_DRIVER_CONF_REG, 0xff); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Enable Vibrator PWM generator %d", + error); + return error; + } + } + + error = HW_REG_WRITE(PWM_VIBNL_CONF_REG, left_speed.negative); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Write Left Vibrator negative PWM %d", error); + goto err_cleanup; + } + + error = HW_REG_WRITE(PWM_VIBPL_CONF_REG, left_speed.positive); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Write Left Vibrator positive PWM %d", error); + goto err_cleanup; + } + + error = HW_REG_WRITE(PWM_VIBNR_CONF_REG, right_speed.negative); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Write Right Vibrator negative PWM %d", error); + goto err_cleanup; + } + + error = HW_REG_WRITE(PWM_VIBPR_CONF_REG, right_speed.positive); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Write Right Vibrator positive PWM %d", error); + goto err_cleanup; + } + + if (!left_speed.negative && !right_speed.negative && + !left_speed.positive && !right_speed.positive) { + error = HW_REG_WRITE(VIB_DRIVER_CONF_REG, 0); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Disable PWM Vibrator generator %d", + error); + goto err_cleanup; + } + + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + 0, (EN_VIBL_MASK|EN_VIBR_MASK)); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Power down Vibrator Class-D driver %d", + error); + goto err_cleanup; + } + + /* Power down audio block */ + error = ste_audio_io_core_api_powerdown_audiocodec( + STE_AUDIOIO_POWER_VIBRA); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "Audio power down failed %d", error); + goto err_cleanup; + } + } + +err_cleanup: + /* Release client */ + if (!left_speed.negative && !right_speed.negative && + !left_speed.positive && !right_speed.positive) { + mutex_lock(&ptr_audio_codec_cnxt->audio_io_mutex); + ste_audioio_vibrator_release(client); + mutex_unlock(&ptr_audio_codec_cnxt->audio_io_mutex); + } + return error; +} +EXPORT_SYMBOL(ste_audioio_vibrator_pwm_control); + +/** + * @brief This function sets FIR coefficients + * @fir_coeffs: pointer to structure audioio_fir_coefficients_t + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_core_api_fir_coeffs_control(struct audioio_fir_coefficients_t + *fir_coeffs) +{ + unsigned char coefficient; + int i, error; + + if (fir_coeffs->start_addr >= STE_AUDIOIO_MAX_COEFFICIENTS) + return -EINVAL; + + mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + + error = HW_REG_WRITE(SIDETONE_FIR_ADDR_REG, fir_coeffs->start_addr); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "FIR start address write failed %d", error); + goto err_cleanup; + } + + for (i = fir_coeffs->start_addr; + i < STE_AUDIOIO_MAX_COEFFICIENTS; i++) { + + coefficient = (fir_coeffs->coefficients[i]>>8) & 0xff; + error = HW_REG_WRITE(SIDETONE_FIR_COEFF_MSB_REG, coefficient); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "FIR coefficient [%d] msb write failed %d", i, error); + goto err_cleanup; + } + + coefficient = fir_coeffs->coefficients[i] & 0xff; + error = HW_REG_WRITE(SIDETONE_FIR_COEFF_LSB_REG, coefficient); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "FIR coefficient [%d] lsb write failed %d", i, error); + goto err_cleanup; + } + } + + error = HW_ACODEC_MODIFY_WRITE(SIDETONE_FIR_ADDR_REG, + APPLY_FIR_COEFFS_MASK, 0); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "FIR coefficients activation failed %d", error); + goto err_cleanup; + } + + error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG, + FIR_FILTERCONTROL, 0); + if (error) { + dev_err(ptr_audio_codec_cnxt->dev, + "ST FIR Filters enable failed %d", error); + goto err_cleanup; + } + +err_cleanup: + mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex)); + return error; +} diff --git a/drivers/misc/audio_io_dev/ste_audio_io_core.h b/drivers/misc/audio_io_dev/ste_audio_io_core.h new file mode 100644 index 00000000000..7d109eb9e83 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_core.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef _AUDIOIO_CORE_H_ +#define _AUDIOIO_CORE_H_ + +#include <mach/ste_audio_io_ioctl.h> +#include "ste_audio_io_func.h" +#include "ste_audio_io_hwctrl_common.h" + +#define MAX_NO_CHANNELS 4 + +#define STE_AUDIOIO_POWER_AUDIO 1 +#define STE_AUDIOIO_POWER_VIBRA 2 + +struct transducer_context_t { + /* public variables */ + int gain[MAX_NO_CHANNELS]; + int is_muted[MAX_NO_CHANNELS]; + int is_power_up[MAX_NO_CHANNELS]; + /* public funcs */ + int (*pwr_up_func)(enum AUDIOIO_CH_INDEX, struct device *); + int (*pwr_down_func)(enum AUDIOIO_CH_INDEX, struct device *); + int (*pwr_state_func)(struct device *); + int (*set_gain_func)(enum AUDIOIO_CH_INDEX, u16, int, u32, + struct device *); + int (*get_gain_func)(int *, int *, u16, struct device *); + int (*mute_func)(enum AUDIOIO_CH_INDEX, struct device *); + int (*unmute_func)(enum AUDIOIO_CH_INDEX, int *, struct device *); + int (*mute_state_func)(struct device *); + int (*enable_fade_func)(struct device *); + int (*disable_fade_func)(struct device *); + int (*switch_to_burst_func)(int, struct device *); + int (*switch_to_normal_func)(struct device *); + int (*enable_loop)(enum AUDIOIO_CH_INDEX, enum AUDIOIO_HAL_HW_LOOPS, + int, struct device *, void *); + int (*disable_loop)(enum AUDIOIO_CH_INDEX, enum AUDIOIO_HAL_HW_LOOPS, + struct device *, void *); +}; + +struct audiocodec_context_t { + int audio_codec_powerup; + int power_client; + int vibra_client; + struct mutex audio_io_mutex; + struct mutex vibrator_mutex; + struct transducer_context_t *transducer[MAX_NO_TRANSDUCERS]; + struct device *dev; + int (*gpio_altf_init) (void); + int (*gpio_altf_exit) (void); +}; + + +int ste_audio_io_core_api_access_read(struct audioio_data_t *dev_data); + +int ste_audio_io_core_api_access_write(struct audioio_data_t *dev_data); + +int ste_audio_io_core_api_power_control_transducer( + struct audioio_pwr_ctrl_t *pwr_ctrl); + +int ste_audio_io_core_api_power_status_transducer( + struct audioio_pwr_ctrl_t *pwr_ctrl); + +int ste_audio_io_core_api_loop_control(struct audioio_loop_ctrl_t *loop_ctrl); + +int ste_audio_io_core_api_loop_status(struct audioio_loop_ctrl_t *loop_ctrl); + +int ste_audio_io_core_api_get_transducer_gain_capability( + struct audioio_get_gain_t *get_gain); + +int ste_audio_io_core_api_gain_capabilities_loop( + struct audioio_gain_loop_t *gain_loop); + +int ste_audio_io_core_api_supported_loops( + struct audioio_support_loop_t *support_loop); + +int ste_audio_io_core_api_gain_descriptor_transducer( + struct audioio_gain_desc_trnsdr_t *gdesc_trnsdr); + +int ste_audio_io_core_api_gain_control_transducer( + struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr); + +int ste_audio_io_core_api_gain_query_transducer( + struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr); + +int ste_audio_io_core_api_mute_control_transducer( + struct audioio_mute_trnsdr_t *mute_trnsdr); + +int ste_audio_io_core_api_mute_status_transducer( + struct audioio_mute_trnsdr_t *mute_trnsdr); + +int ste_audio_io_core_api_fading_control(struct audioio_fade_ctrl_t *fade_ctrl); + +int ste_audio_io_core_api_burstmode_control( + struct audioio_burst_ctrl_t *burst_ctrl); + +int ste_audio_io_core_api_powerup_audiocodec(int power_client); + +int ste_audio_io_core_api_powerdown_audiocodec(int power_client); + +int ste_audio_io_core_api_init_data(struct platform_device *pdev); + +bool ste_audio_io_core_is_ready_for_suspend(void); +void ste_audio_io_core_api_free_data(void); + +int ste_audio_io_core_api_fsbitclk_control( + struct audioio_fsbitclk_ctrl_t *fsbitclk_ctrl); +int ste_audio_io_core_api_pseudoburst_control( + struct audioio_pseudoburst_ctrl_t *pseudoburst_ctrl); + +void ste_audio_io_core_api_store_data(enum AUDIOIO_CH_INDEX channel_index, + int *ptr, int value); + +int ste_audioio_vibrator_alloc(int client, int mask); + +void ste_audioio_vibrator_release(int client); + +enum AUDIOIO_COMMON_SWITCH ste_audio_io_core_api_get_status( + enum AUDIOIO_CH_INDEX channel_index, int *ptr); + +int ste_audio_io_core_api_acodec_power_control(struct audioio_acodec_pwr_ctrl_t + *audio_acodec_pwr_ctrl); + +int ste_audio_io_core_api_fir_coeffs_control(struct audioio_fir_coefficients_t + *fir_coeffs); + +int ste_audio_io_core_debug(int x); + +#endif /* _AUDIOIO_CORE_H_ */ + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_dev.c b/drivers/misc/audio_io_dev/ste_audio_io_dev.c new file mode 100644 index 00000000000..8b677909104 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_dev.c @@ -0,0 +1,738 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include "ste_audio_io_dev.h" + +#define STR_DEBUG_ON "debug on" +#define AUDIOIO_DEVNAME "ab8500-codec" + +static int ste_audio_io_open(struct inode *inode, struct file *filp); +static int ste_audio_io_release(struct inode *inode, struct file *filp); +static long ste_audio_io_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +static int ste_audio_io_cmd_parser(unsigned int cmd, unsigned long arg); +static ssize_t ste_audio_io_write(struct file *filp, + const char __user *buf, size_t count, loff_t *f_pos); + + +/** + * @brief Check IOCTL type, command no and access direction + * @ inode value corresponding to the file descriptor + * @file value corresponding to the file descriptor + * @cmd IOCTL command code + * @arg Command argument + * @return 0 on success otherwise negative error code + */ +static long ste_audio_io_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int retval = 0; + int err = 0; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != AUDIOIO_IOC_MAGIC) + return -ENOTTY; + + /* IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + retval = ste_audio_io_cmd_parser(cmd, arg); + + return retval; +} +/** + * @brief IOCTL call to read the value from AB8500 device + * @cmd IOCTL command code + * @arg Command argument + * @return 0 on success otherwise negative error code + */ + +static int process_read_register_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_data_t *audio_dev_data; + + audio_dev_data = (struct audioio_data_t *)&cmd_data; + + if (copy_from_user(audio_dev_data, (void __user *)arg, + sizeof(struct audioio_data_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_access_read(audio_dev_data); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_dev_data, + sizeof(struct audioio_data_t))) + return -EFAULT; + return 0; +} +/** + * @brief IOCTL call to write the given value to the AB8500 device + * @cmd IOCTL command code + * @arg Command argument + * @return 0 on success otherwise negative error code + */ + +static int process_write_register_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_data_t *audio_dev_data; + + audio_dev_data = (struct audioio_data_t *)&cmd_data; + + if (copy_from_user(audio_dev_data, (void __user *)arg, + sizeof(struct audioio_data_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_access_write(audio_dev_data); + + return retval; +} +/** + * @brief IOCTL call to control the power on/off of hardware components + * @cmd IOCTL command code + * @arg Command argument + * @return 0 on success otherwise negative error code + */ + +static int process_pwr_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_pwr_ctrl_t *audio_pwr_ctrl; + + audio_pwr_ctrl = (struct audioio_pwr_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_pwr_ctrl, (void __user *)arg, + sizeof(struct audioio_pwr_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_power_control_transducer(audio_pwr_ctrl); + + return retval; +} + +static int process_pwr_sts_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_pwr_ctrl_t *audio_pwr_sts; + + audio_pwr_sts = (struct audioio_pwr_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_pwr_sts, (void __user *)arg, + sizeof(struct audioio_pwr_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_power_status_transducer(audio_pwr_sts); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_pwr_sts, + sizeof(struct audioio_pwr_ctrl_t))) + return -EFAULT; + + return 0; +} + +static int process_lp_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_loop_ctrl_t *audio_lp_ctrl; + + audio_lp_ctrl = (struct audioio_loop_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_lp_ctrl, (void __user *)arg, + sizeof(struct audioio_loop_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_loop_control(audio_lp_ctrl); + + return retval; +} + +static int process_lp_sts_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_loop_ctrl_t *audio_lp_sts; + + audio_lp_sts = (struct audioio_loop_ctrl_t *)&cmd_data; + + + if (copy_from_user(audio_lp_sts, (void __user *)arg, + sizeof(struct audioio_loop_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_loop_status(audio_lp_sts); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_lp_sts, + sizeof(struct audioio_loop_ctrl_t))) + return -EFAULT; + return 0; +} + +static int process_get_trnsdr_gain_capability_cmd(unsigned int cmd, + unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_get_gain_t *audio_trnsdr_gain; + + audio_trnsdr_gain = (struct audioio_get_gain_t *)&cmd_data; + + if (copy_from_user(audio_trnsdr_gain, (void __user *)arg, + sizeof(struct audioio_get_gain_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_get_transducer_gain_capability( + audio_trnsdr_gain); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_trnsdr_gain, + sizeof(struct audioio_get_gain_t))) + return -EFAULT; + return 0; +} + +static int process_gain_cap_loop_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_gain_loop_t *audio_gain_loop; + + audio_gain_loop = (struct audioio_gain_loop_t *)&cmd_data; + + if (copy_from_user(audio_gain_loop, (void __user *)arg, + sizeof(struct audioio_gain_loop_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_gain_capabilities_loop(audio_gain_loop); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_gain_loop, + sizeof(struct audioio_gain_loop_t))) + return -EFAULT; + return 0; +} + + +static int process_support_loop_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_support_loop_t *audio_spprt_loop; + + audio_spprt_loop = (struct audioio_support_loop_t *)&cmd_data; + + if (copy_from_user(audio_spprt_loop, (void __user *)arg, + sizeof(struct audioio_support_loop_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_supported_loops(audio_spprt_loop); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_spprt_loop, + sizeof(struct audioio_support_loop_t))) + return -EFAULT; + return 0; +} + + +static int process_gain_desc_trnsdr_cmd(unsigned int cmd, unsigned long arg) + +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_gain_desc_trnsdr_t *audio_gain_desc; + + audio_gain_desc = (struct audioio_gain_desc_trnsdr_t *)&cmd_data; + + if (copy_from_user(audio_gain_desc, (void __user *)arg, + sizeof(struct audioio_gain_desc_trnsdr_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_gain_descriptor_transducer( + audio_gain_desc); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_gain_desc, + sizeof(struct audioio_gain_desc_trnsdr_t))) + return -EFAULT; + return 0; +} + + +static int process_gain_ctrl_trnsdr_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_gain_ctrl_trnsdr_t *audio_gain_ctrl; + + audio_gain_ctrl = (struct audioio_gain_ctrl_trnsdr_t *)&cmd_data; + + if (copy_from_user(audio_gain_ctrl, (void __user *)arg, + sizeof(struct audioio_gain_ctrl_trnsdr_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_gain_control_transducer( + audio_gain_ctrl); + + return retval; +} + +static int process_gain_query_trnsdr_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_gain_ctrl_trnsdr_t *audio_gain_query; + + audio_gain_query = (struct audioio_gain_ctrl_trnsdr_t *)&cmd_data; + + if (copy_from_user(audio_gain_query, (void __user *)arg, + sizeof(struct audioio_gain_ctrl_trnsdr_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_gain_query_transducer(audio_gain_query); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_gain_query, + sizeof(struct audioio_gain_ctrl_trnsdr_t))) + return -EFAULT; + return 0; +} + +static int process_mute_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_mute_trnsdr_t *audio_mute_ctrl; + + audio_mute_ctrl = (struct audioio_mute_trnsdr_t *)&cmd_data; + if (copy_from_user(audio_mute_ctrl , (void __user *)arg, + sizeof(struct audioio_mute_trnsdr_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_mute_control_transducer( + audio_mute_ctrl); + + return retval; +} + +static int process_mute_sts_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_mute_trnsdr_t *audio_mute_sts; + + audio_mute_sts = (struct audioio_mute_trnsdr_t *)&cmd_data; + + if (copy_from_user(audio_mute_sts, (void __user *)arg, + sizeof(struct audioio_mute_trnsdr_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_mute_status_transducer(audio_mute_sts); + if (0 != retval) + return retval; + + if (copy_to_user((void __user *)arg, audio_mute_sts, + sizeof(struct audioio_mute_trnsdr_t))) + return -EFAULT; + return 0; +} + +static int process_fade_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_fade_ctrl_t *audio_fade; + audio_fade = (struct audioio_fade_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_fade , (void __user *)arg, + sizeof(struct audioio_fade_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_fading_control(audio_fade); + + return retval; +} + +static int process_burst_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_burst_ctrl_t *audio_burst; + + audio_burst = (struct audioio_burst_ctrl_t *)&cmd_data; + if (copy_from_user(audio_burst , (void __user *)arg, + sizeof(struct audioio_burst_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_burstmode_control(audio_burst); + + return retval; + + return 0; +} + +static int process_fsbitclk_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_fsbitclk_ctrl_t *audio_fsbitclk; + + audio_fsbitclk = (struct audioio_fsbitclk_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_fsbitclk , (void __user *)arg, + sizeof(struct audioio_fsbitclk_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_fsbitclk_control(audio_fsbitclk); + + return retval; + + return 0; + +} + +static int process_pseudoburst_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_pseudoburst_ctrl_t *audio_pseudoburst; + + audio_pseudoburst = (struct audioio_pseudoburst_ctrl_t *)&cmd_data; + + if (copy_from_user(audio_pseudoburst , (void __user *)arg, + sizeof(struct audioio_pseudoburst_ctrl_t))) + return -EFAULT; + + retval = ste_audio_io_core_api_pseudoburst_control(audio_pseudoburst); + + return retval; + + return 0; + +} +static int process_audiocodec_pwr_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + union audioio_cmd_data_t cmd_data; + struct audioio_acodec_pwr_ctrl_t *audio_acodec_pwr_ctrl; + audio_acodec_pwr_ctrl = (struct audioio_acodec_pwr_ctrl_t *)&cmd_data; + if (copy_from_user(audio_acodec_pwr_ctrl, (void __user *)arg, + sizeof(struct audioio_acodec_pwr_ctrl_t))) + return -EFAULT; + retval = ste_audio_io_core_api_acodec_power_control( + audio_acodec_pwr_ctrl); + return retval; +} + +static int process_fir_coeffs_ctrl_cmd(unsigned int cmd, unsigned long arg) +{ + int retval; + struct audioio_fir_coefficients_t *cmd_data; + cmd_data = kmalloc(sizeof(struct audioio_fir_coefficients_t), + GFP_KERNEL); + if (!cmd_data) + return -ENOMEM; + if (copy_from_user(cmd_data, (void __user *)arg, + sizeof(struct audioio_fir_coefficients_t))) { + kfree(cmd_data); + return -EFAULT; + } + retval = ste_audio_io_core_api_fir_coeffs_control(cmd_data); + kfree(cmd_data); + return retval; +} + +static int ste_audio_io_cmd_parser(unsigned int cmd, unsigned long arg) +{ + int retval = 0; + + switch (cmd) { + case AUDIOIO_READ_REGISTER: + retval = process_read_register_cmd(cmd, arg); + break; + + case AUDIOIO_WRITE_REGISTER: + retval = process_write_register_cmd(cmd, arg); + break; + + case AUDIOIO_PWR_CTRL_TRNSDR: + retval = process_pwr_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_PWR_STS_TRNSDR: + retval = process_pwr_sts_cmd(cmd, arg); + break; + + case AUDIOIO_LOOP_CTRL: + retval = process_lp_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_LOOP_STS: + retval = process_lp_sts_cmd(cmd, arg); + break; + + case AUDIOIO_GET_TRNSDR_GAIN_CAPABILITY: + retval = process_get_trnsdr_gain_capability_cmd(cmd, arg); + break; + + case AUDIOIO_GAIN_CAP_LOOP: + retval = process_gain_cap_loop_cmd(cmd, arg); + break; + + case AUDIOIO_SUPPORT_LOOP: + retval = process_support_loop_cmd(cmd, arg); + break; + + case AUDIOIO_GAIN_DESC_TRNSDR: + retval = process_gain_desc_trnsdr_cmd(cmd, arg); + break; + + case AUDIOIO_GAIN_CTRL_TRNSDR: + retval = process_gain_ctrl_trnsdr_cmd(cmd, arg); + break; + + case AUDIOIO_GAIN_QUERY_TRNSDR: + retval = process_gain_query_trnsdr_cmd(cmd, arg); + break; + + case AUDIOIO_MUTE_CTRL_TRNSDR: + retval = process_mute_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_MUTE_STS_TRNSDR: + retval = process_mute_sts_cmd(cmd, arg); + break; + + case AUDIOIO_FADE_CTRL: + retval = process_fade_cmd(cmd, arg); + break; + + case AUDIOIO_BURST_CTRL: + retval = process_burst_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_FSBITCLK_CTRL: + retval = process_fsbitclk_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_PSEUDOBURST_CTRL: + retval = process_pseudoburst_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_AUDIOCODEC_PWR_CTRL: + retval = process_audiocodec_pwr_ctrl_cmd(cmd, arg); + break; + + case AUDIOIO_FIR_COEFFS_CTRL: + retval = process_fir_coeffs_ctrl_cmd(cmd, arg); + break; + } + return retval; +} + +static int ste_audio_io_open(struct inode *inode, struct file *filp) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + return 0; +} + +static int ste_audio_io_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t ste_audio_io_write(struct file *filp, + const char __user *buf, size_t count, loff_t *f_pos) +{ + char *x = kmalloc(count, GFP_KERNEL); + int debug_flag = 0; + + if (copy_from_user(x, buf, count)) + return -EFAULT; + + if (count >= strlen(STR_DEBUG_ON)) { + + if (!strncmp(STR_DEBUG_ON, x, strlen(STR_DEBUG_ON))) + debug_flag = 1; + else + debug_flag = 0; + } + + ste_audio_io_core_debug(debug_flag); + + kfree(x); + + return count; +} + +static const struct file_operations ste_audio_io_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = ste_audio_io_ioctl, + .open = ste_audio_io_open, + .release = ste_audio_io_release, + .write = ste_audio_io_write, +}; + +/** + * audio_io_misc_dev - Misc device config for audio_io + */ +static struct miscdevice audio_io_misc_dev = { + MISC_DYNAMIC_MINOR, + "audioio", + &ste_audio_io_fops +}; + +/** + * ste_audio_io_probe() - probe the device + * @pdev: pointer to the platform device structure + * + * This funtion is called after the driver is registered to platform + * device framework. It does allocate the memory for the internal + * data structure and intialized core APIs. + */ +static int ste_audio_io_drv_probe(struct platform_device *pdev) +{ + int error; + + ste_audio_io_device = pdev; + + dev_dbg(&ste_audio_io_device->dev, "ste_audio_io device probe\n"); + + error = misc_register(&audio_io_misc_dev); + if (error) { + printk(KERN_WARNING "%s: registering misc device failed\n", + __func__); + return error; + } + + error = ste_audio_io_core_api_init_data(ste_audio_io_device); + if (error < 0) { + dev_err(&ste_audio_io_device->dev, + "ste_audioio_core_api_init_data failed err = %d", + error); + goto ste_audio_io_misc_deregister; + } + return 0; + +ste_audio_io_misc_deregister: + misc_deregister(&audio_io_misc_dev); + return error; +} + +/** + * ste_audio_io_remove() - Removes the device + * @pdev: pointer to the platform_device structure + * + * This function is called when this mnodule is removed using rmmod + */ +static int ste_audio_io_drv_remove(struct platform_device *pdev) +{ + ste_audio_io_core_api_free_data(); + misc_deregister(&audio_io_misc_dev); + return 0; +} + +/** + * ste_audio_io_drv_suspend - suspend audio_io + * @pdev: platform data + * @state: power down level + */ +static int ste_audio_io_drv_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (ste_audio_io_core_is_ready_for_suspend()) + return 0; + else + return -EINVAL; +} + +/** + * ste_audio_io_drv_resume - put back audio_io in the normal state + * @pdev: platform data + */ +static int ste_audio_io_drv_resume(struct platform_device *pdev) +{ + return 0; +} + +/** + * struct audio_io_driver: audio_io platform structure + * @probe: The probe funtion to be called + * @remove: The remove funtion to be called + * @resume: The resume function to be called + * @suspend: The suspend function to be called + * @driver: The driver data + */ +static struct platform_driver ste_audio_io_driver = { + .probe = ste_audio_io_drv_probe, + .remove = ste_audio_io_drv_remove, + .driver = { + .name = AUDIOIO_DEVNAME, + .owner = THIS_MODULE, + }, + .suspend = ste_audio_io_drv_suspend, + .resume = ste_audio_io_drv_resume, +}; + +/** Pointer to platform device needed to access abx500 core functions */ +struct platform_device *ste_audio_io_device; + +static int __init ste_audio_io_init(void) +{ + return platform_driver_register(&ste_audio_io_driver); +} +module_init(ste_audio_io_init); + +static void __exit ste_audio_io_exit(void) +{ + platform_driver_unregister(&ste_audio_io_driver); +} +module_exit(ste_audio_io_exit); + +MODULE_AUTHOR("Deepak KARDA <deepak.karda@stericsson.com>"); +MODULE_DESCRIPTION("STE_AUDIO_IO"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/audio_io_dev/ste_audio_io_dev.h b/drivers/misc/audio_io_dev/ste_audio_io_dev.h new file mode 100644 index 00000000000..bcb9dce3ad2 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_dev.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef _AUDIOIO_DEV_H_ +#define _AUDIOIO_DEV_H_ + +#include <mach/ste_audio_io_ioctl.h> +#include "ste_audio_io_core.h" + +union audioio_cmd_data_t { + struct audioio_burst_ctrl_t audioio_burst_ctrl; + struct audioio_fade_ctrl_t audioio_fade_ctrl; + struct audioio_mute_trnsdr_t audioio_mute_trnsdr; + struct audioio_gain_ctrl_trnsdr_t audioio_gain_ctrl_trnsdr; + struct audioio_gain_desc_trnsdr_t audioio_gain_desc_trnsdr; + struct audioio_support_loop_t audioio_support_loop; + struct audioio_gain_loop_t audioio_gain_loop; + struct audioio_get_gain_t audioio_get_gain; + struct audioio_loop_ctrl_t audioio_loop_ctrl; + struct audioio_pwr_ctrl_t audioio_pwr_ctrl; + struct audioio_data_t audioio_data; + struct audioio_fsbitclk_ctrl_t audioio_fsbitclk_ctrl; + struct audioio_acodec_pwr_ctrl_t audioio_acodec_pwr_ctrl; + struct audioio_pseudoburst_ctrl_t audioio_pseudoburst_ctrl; +}; + + +#endif /* _AUDIOIO_DEV_H_ */ + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_func.c b/drivers/misc/audio_io_dev/ste_audio_io_func.c new file mode 100644 index 00000000000..7238085938e --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_func.c @@ -0,0 +1,4371 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#include <linux/gpio.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <mach/ste_audio_io_vibrator.h> +#include <mach/ste_audio.h> +#include "ste_audio_io_func.h" +#include "ste_audio_io_core.h" +#include "ste_audio_io_ab8500_reg_defs.h" +#include "ste_audio_io_hwctrl_common.h" + +static struct clk *clk_ptr_msp0; +static int bluetooth_power_up_count; +static int acodec_reg_dump; + +#define NCP_TIMEOUT 200 /* 200 ms */ +/* + * TODO: Use proper register defines instead of home-made generic ones. + */ +#define SHIFT_QUARTET0 0 +#define SHIFT_QUARTET1 4 +#define MASK_QUARTET (0xFUL) +#define MASK_QUARTET1 (MASK_QUARTET << SHIFT_QUARTET1) +#define MASK_QUARTET0 (MASK_QUARTET << SHIFT_QUARTET0) + +/** + * @brief Modify the specified register + * @reg Register + * @mask_set Bit to be set + * @mask_clear Bit to be cleared + * @return 0 on success otherwise negative error code + */ + +unsigned int ab8500_acodec_modify_write(unsigned int reg, u8 mask_set, + u8 mask_clear) +{ + u8 value8, retval = 0; + value8 = HW_REG_READ(reg); + /* clear the specified bit */ + value8 &= ~mask_clear; + /* set the asked bit */ + value8 |= mask_set; + retval = HW_REG_WRITE(reg, value8); + return retval; +} + +/** + * @brief Power up headset on a specific channel + * @channel_index Channel-index of headset + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_headset(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + unsigned long end_time; + + /* Check if HS PowerUp request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "HS should have mono or stereo channels"); + return -EINVAL; + } + + ste_audio_io_mute_headset(channel_index, dev); + + error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG, + HS_AUTO_EN, 0); + if (0 != error) { + dev_err(dev, "NCP fully controlled with EnCpHs bit %d", error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG, + (EN_NEG_CP|HS_AUTO_EN), 0); + if (0 != error) { + dev_err(dev, "Enable Negative Charge Pump %d", error); + return error; + } + + /* Wait for negative charge pump to start */ + end_time = jiffies + msecs_to_jiffies(NCP_TIMEOUT); + while (!(HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK) + && time_after_eq(end_time, jiffies)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + if (!(HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK)) { + error = -EFAULT; + dev_err(dev, "Negative Charge Pump start error % d", error); + return error; + } + + /* Enable DA1 for HSL */ + if (channel_index & e_CHANNEL_1) { + + /* Power Up HSL driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + EN_HSL_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up HSL Driver %d", error); + return error; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + if (EN_DA1 & initialVal_DA) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, + SLOT08_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA1 from Slot 08 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + DA1_TO_HSL, 0); + if (0 != error) { + dev_err(dev, + "DA_IN1 path mixed with sidetone FIR %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA1, 0); + if (0 != error) { + dev_err(dev, "Power up HSL %d ", error); + return error; + } + + /* Power Up HSL DAC driver */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, + POWER_UP_HSL_DAC, 0); + if (0 != error) { + dev_err(dev, "Power Up HSL DAC driver %d", error); + return error; + } + + /* Power up HSL DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_HSL_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up HSL DAC and digital path %d", + error); + return error; + } + + /* + * Disable short detection. Pull Down output to ground, + * Use local oscillator, Gain change without zero cross control + */ + error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG, + HS_SHORT_DIS|HS_PULL_DOWN_EN|HS_OSC_EN|HS_ZCD_DIS, 0); + if (0 != error) { + dev_err(dev, "Disable short detection." + "Pull Down output to ground,Use local oscillator,Gain" + "change without zero cross control %d", error); + return error; + } + } + + /* Enable DA2 for HSR */ + if (channel_index & e_CHANNEL_2) { + + /* Power Up HSR driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + EN_HSR_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up HSR Driver %d", error); + return error; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (EN_DA2 & initialVal_DA) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA2_REG, + SLOT09_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, + "Data sent to DA2 from Slot 09 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, DA2_TO_HSR, + 0); + if (0 != error) { + dev_err(dev, + "DA_IN2 path mixed with sidetone FIR %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA2, 0); + if (0 != error) { + dev_err(dev, "Power up HSR %d ", error); + return error; + } + + /* Power Up HSR DAC driver */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, + POWER_UP_HSR_DAC, 0); + if (0 != error) { + dev_err(dev, "Power Up HSR DAC driver %d", error); + return error; + } + + /* Power up HSR DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_HSR_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up HSR DAC and digital path %d", + error); + return error; + } + + /* + * TEST START .havent cleared the bits in power down.Disable short + * detection. Pull Down output to ground, Use local oscillator, + * Gain change without zero cross control + */ + + error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG, + HS_SHORT_DIS|HS_PULL_DOWN_EN|HS_OSC_EN|HS_ZCD_DIS, 0); + if (0 != error) { + dev_err(dev, "Disable short detection." + "Pull Down output to ground, Use local oscillator," + "Gain change without zero cross control %d", error); + return error; + } + /* TEST END */ + } + ste_audio_io_unmute_headset(channel_index, 0, dev); + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief Power down headset on a specific channel + * @channel_index Channel-index of headset + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_headset(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + unsigned long end_time; + + /* Check if HS Power Down request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "HS should have mono or stereo channels"); + return -EINVAL; + } + + /* Disable Negative Charge Pump */ + error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG, + (EN_NEG_CP|HS_AUTO_EN), 0); + if (0 != error) { + dev_err(dev, "NCP not fully controlled with EnCpHs bit %d", + error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG, 0, + EN_NEG_CP); + if (0 != error) { + dev_err(dev, "Disable Negative Charge Pump %d", error); + return error; + } + + /* Wait for negative charge pump to stop */ + end_time = jiffies + msecs_to_jiffies(NCP_TIMEOUT); + while ((HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK) + && time_after_eq(end_time, jiffies)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + if (HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK) { + error = -EFAULT; + dev_err(dev, "Negative Charge Pump stop error % d", error); + return error; + } + + if (channel_index & e_CHANNEL_1) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_DA & EN_DA1)) + return 0; + + /* Power Down HSL driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0, + EN_HSL_MASK); + if (0 != error) { + dev_err(dev, "Power down HSL Driver %d", error); + return error; + } + + /* Power Down HSL DAC driver */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + POWER_UP_HSL_DAC); + if (0 != error) { + dev_err(dev, "Power Up HSL DAC Driver %d", error); + return error; + } + + /* Power Down HSL DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_HSL_MASK); + if (0 != error) { + dev_err(dev, + "Power down HSL DAC and digital path %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA1); + if (0 != error) { + dev_err(dev, "Disable DA1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + 0, DA1_TO_HSL); + if (0 != error) { + dev_err(dev, + "Clear DA_IN1 path mixed with sidetone FIR %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, 0, + SLOT08_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA1 cleared from Slot 08 %d", + error); + return error; + } + + + } + /* Enable DA2 for HSR */ + + if (channel_index & e_CHANNEL_2) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_DA & EN_DA2)) + return 0; + + /* Power Down HSR driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0, + EN_HSR_MASK); + if (0 != error) { + dev_err(dev, "Power down HSR Driver %d", error); + return error; + } + + /* Power Down HSR DAC driver */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + POWER_UP_HSR_DAC); + if (0 != error) { + dev_err(dev, "Power down HSR DAC Driver %d", error); + return error; + } + + /* Power Down HSR DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_HSR_MASK); + if (0 != error) { + dev_err(dev, + "Power down HSR DAC and digital path %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA2); + if (0 != error) { + dev_err(dev, "Disable DA2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + DA2_TO_HSR); + if (0 != error) { + dev_err(dev, + "Clear DA_IN2 path mixed with sidetone FIR %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA2_REG, 0, + SLOT09_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA2 cleared from Slot 09 %d", + error); + return error; + } + + } + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief Mute headset on a specific channel + * @channel_index Headeset channel-index + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_mute_headset(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + /* Check if HS Mute request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "HS should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + /* Mute HSL */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, + EN_HSL_MASK | EN_HSL_DAC_MASK, + 0); + if (0 != error) { + dev_err(dev, "Mute HSL %d", error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* Mute HSR */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, + EN_HSR_MASK | EN_HSR_DAC_MASK, + 0); + if (0 != error) { + dev_err(dev, "Mute HSR %d", error); + return error; + } + } + + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief Unmute headset on a specific channel + * @channel_index Headeset channel-index + * @gain Gain index of headset + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_unmute_headset(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + + /* Check if HS UnMute request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "HS should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + /* UnMute HSL */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0, + EN_HSL_MASK | EN_HSL_DAC_MASK); + if (0 != error) { + dev_err(dev, "UnMute HSL %d", error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* UnMute HSR */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0, + EN_HSR_MASK | EN_HSR_DAC_MASK); + if (0 != error) { + dev_err(dev, "UnMute HSR %d", error); + return error; + } + } + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief Enables fading of headset on a specific channel + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_fade_headset(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG, + 0, DIS_HS_FAD); + if (0 != error) { + dev_err(dev, "Enable fading for HS %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for HSL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG, 0, + DIS_DIG_GAIN_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for Digital Gain of HSL %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA2_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for HSR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(HSR_DIGITAL_GAIN_REG, 0, + DIS_DIG_GAIN_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for Digital Gain of HSR %d", + error); + return error; + } + + return error; +} +/** + * @brief Disables fading of headset on a specific channel + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_disable_fade_headset(struct device *dev) +{ + int error = 0; + error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG, + DIS_HS_FAD, 0); + if (0 != error) { + dev_err(dev, "Disable fading for HS %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for HSL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG, + DIS_DIG_GAIN_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for Digital Gain of HSL %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA2_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for HSR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(HSR_DIGITAL_GAIN_REG, + DIS_DIG_GAIN_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for Digital Gain of HSR %d", + error); + return error; + } + return error; +} +/** + * @brief Power up earpiece + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_up_earpiece(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if Earpiece PowerUp request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "EARPIECE should have mono channel"); + return -EINVAL; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if Earpiece is already powered up or DA1 being used by HS */ + if (EN_DA1 & initialVal_DA) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + DA1_TO_HSL, 0); + if (0 != error) { + dev_err(dev, + "DA_IN1 path mixed with sidetone FIR %d", error); + return error; + } + + /* Enable DA1 */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, + SLOT08_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA1 from Slot 08 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA1, 0); + if (0 != error) { + dev_err(dev, "Enable DA1 %d", error); + return error; + } + + /* Power Up EAR class-AB driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + EN_EAR_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up EAR class-AB driver %d", error); + return error; + } + + /* Power up EAR DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE( + DIGITAL_OUTPUT_ENABLE_REG, EN_EAR_MASK, 0); + if (0 != error) { + dev_err(dev, "Power up EAR DAC and digital path %d", error); + return error; + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Power down earpiece + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_earpiece(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if Earpiece PowerDown request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "EARPIECE should have mono channel"); + return -EINVAL; + } + + /* Check if Earpiece is already powered down or DA1 being used by HS */ + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_DA & EN_DA1)) + return 0; + + /* Power Down EAR class-AB driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + 0, EN_EAR_MASK); + if (0 != error) { + dev_err(dev, "Power Down EAR class-AB driver %d", error); + return error; + } + + /* Power Down EAR DAC and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + 0, EN_EAR_MASK); + if (0 != error) { + dev_err(dev, + "Power Down EAR DAC and digital path %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, DA1_TO_HSL); + if (0 != error) { + dev_err(dev, + "Clear DA_IN1 path mixed with sidetone FIR %d", + error); + return error; + } + + /* Disable DA1 */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA1); + if (0 != error) { + dev_err(dev, "Disable DA1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, 0, + SLOT08_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA1 cleared from Slot 08 %d", error); + return error; + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Mute earpiece + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_mute_earpiece(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + + /* Check if Earpiece Mute request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "EARPIECE should have mono channel"); + return -EINVAL; + } + + /* Mute Earpiece */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, + EN_EAR_MASK | EN_EAR_DAC_MASK, 0); + if (0 != error) { + dev_err(dev, "Mute Earpiece %d", error); + return error; + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Unmute earpiece + * @channel_index Channel-index + * @gain Gain index of earpiece + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_unmute_earpiece(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + + /* Check if Earpiece UnMute request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "EARPIECE should have mono channel"); + return -EINVAL; + } + + /* UnMute Earpiece */ + error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0, + EN_EAR_MASK | EN_EAR_DAC_MASK); + if (0 != error) { + dev_err(dev, "UnMute Earpiece %d", error); + return error; + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Enables fading of earpiece + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_fade_earpiece(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for Ear %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG, 0, + DIS_DIG_GAIN_FADING); + if (0 != error) { + dev_err(dev, + "Enable fading for Digital Gain of Ear %d", error); + return error; + } + + return error; +} +/** + * @brief Disables fading of earpiece + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_fade_earpiece(struct device *dev) +{ + int error = 0; + error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for Ear %d", error); + return error; + } + return error; +} +/** + * @brief Power up IHF on a specific channel + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_ihf(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if IHF PowerUp request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "IHF should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (EN_DA3 & initialVal_DA) + return 0; + + /* Enable DA3 for IHFL */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA3_REG, + SLOT10_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA3 from Slot 10 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA3, 0); + if (0 != error) { + dev_err(dev, "Power up IHFL %d", error); + return error; + } + + /* Power Up HFL Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + EN_HFL_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up HFL Class-D Driver %d", error); + return error; + } + + /* Power up HFL Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_HFL_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up HFL Class D driver & digital path %d", + error); + return error; + } + } + + /* Enable DA4 for IHFR */ + if (channel_index & e_CHANNEL_2) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (EN_DA4 & initialVal_DA) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA4_REG, + SLOT11_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA4 from Slot 11 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA4, 0); + if (0 != error) { + dev_err(dev, "Enable DA4 %d", error); + return error; + } + + /* Power Up HFR Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + EN_HFR_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up HFR Class-D Driver %d", error); + return error; + } + + /* Power up HFR Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_HFR_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up HFR Class D driver and digital path %d", + error); + return error; + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Power down IHF on a specific channel + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_ihf(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if IHF Power Down request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "IHF should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_DA & EN_DA3)) + return 0; + + /* Power Down HFL Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0, + EN_HFL_MASK); + if (0 != error) { + dev_err(dev, "Power Down HFL Class-D Driver %d", + error); + return error; + } + + /* Power Down HFL Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_HFL_MASK); + if (0 != error) { + dev_err(dev, + "Power Down HFL Class D driver & digital path %d", + error); + return error; + } + + /* Disable DA3 for IHFL */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA3); + if (0 != error) { + dev_err(dev, "Disable DA3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA3_REG, 0, + SLOT10_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA3 cleared from Slot 10 %d", + error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if IHF is already powered Down */ + if (!(initialVal_DA & EN_DA4)) + return 0; + + /* Power Down HFR Class-D Driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0, + EN_HFR_MASK); + if (0 != error) { + dev_err(dev, "Power Down HFR Class-D Driver %d", + error); + return error; + } + + /* Power Down HFR Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_HFR_MASK); + if (0 != error) { + dev_err(dev, + "Power Down HFR Class D driver & digital path %d", + error); + return error; + } + + /* Disable DA4 for IHFR */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA4); + if (0 != error) { + dev_err(dev, "Disable DA4 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA4_REG, 0, + SLOT11_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA4 cleared from Slot 11 %d", + error); + return error; + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Mute IHF on a specific channel + * @channel_index Channel-index + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_mute_ihf(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + + if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + error = ste_audio_io_set_ihf_gain(channel_index, 0, -63, + 0, dev); + if (0 != error) { + dev_err(dev, "Mute ihf %d", error); + return error; + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Unmute IHF on a specific channel + * @channel_index Channel-index + * @gain Gain index of IHF + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_unmute_ihf(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + + if (channel_index & e_CHANNEL_1) { + error = ste_audio_io_set_ihf_gain(channel_index, 0, gain[0], + 0, dev); + if (0 != error) { + dev_err(dev, "UnMute ihf %d", error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + error = ste_audio_io_set_ihf_gain(channel_index, 0, gain[1], + 0, dev); + if (0 != error) { + dev_err(dev, "UnMute ihf %d", error); + return error; + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Enable fading of IHF + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_enable_fade_ihf(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA3_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for HFL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA4_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for HFR %d", error); + return error; + } + return error; +} +/** + * @brief Disable fading of IHF + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_disable_fade_ihf(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA3_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for HFL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DA4_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for HFR %d", error); + return error; + } + return error; +} +/** + * @brief Power up VIBL + * @channel_index Channel-index of VIBL + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_vibl(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if VibL PowerUp request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "VibL should have mono channel"); + return -EINVAL; + } + + /* Try to allocate vibrator for audio left channel */ + error = ste_audioio_vibrator_alloc(STE_AUDIOIO_CLIENT_AUDIO_L, + STE_AUDIOIO_CLIENT_AUDIO_R | STE_AUDIOIO_CLIENT_AUDIO_L); + if (error) { + dev_err(dev, " Couldn't allocate vibrator %d, client %d", + error, STE_AUDIOIO_CLIENT_AUDIO_L); + return error; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if VibL is already powered up */ + if (initialVal_DA & EN_DA5) + return 0; + + /* Enable DA5 for vibl */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, + SLOT12_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA5 from Slot 12 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + EN_DA5, 0); + if (0 != error) { + dev_err(dev, "Enable DA5 for VibL %d", error); + return error; + } + + /* Power Up VibL Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE( + ANALOG_OUTPUT_ENABLE_REG, EN_VIBL_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up VibL Class-D Driver %d", error); + return error; + } + + /* Power up VibL Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_VIBL_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up VibL Class D driver and digital path %d", + error); + return error; + } + return error; +} +/** + * @brief Power down VIBL + * @channel_index Channel-index of VIBL + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_vibl(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if VibL Power Down request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "VibL should have mono channel"); + return -EINVAL; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if VibL is already powered down */ + if (!(initialVal_DA & EN_DA5)) + return 0; + + + /* Power Down VibL Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, + 0, EN_VIBL_MASK); + if (0 != error) { + dev_err(dev, "Power Down VibL Class-D Driver %d", error); + return error; + } + + /* Power Down VibL Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_VIBL_MASK); + if (0 != error) { + dev_err(dev, + "Power Down VibL Class D driver & digital path %d", + error); + return error; + } + + /* Disable DA5 for VibL */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA5); + if (0 != error) { + dev_err(dev, "Disable DA5 for VibL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0, + SLOT12_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Data sent to DA5 cleared from Slot 12 %d", error); + return error; + } + + /* Release vibrator */ + ste_audioio_vibrator_release(STE_AUDIOIO_CLIENT_AUDIO_L); + + return error; +} +/** + * @brief Enable fading of VIBL + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_fade_vibl(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA5_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for VibL %d", error); + return error; + } + return error; +} +/** + * @brief Disable fading of VIBL + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_fade_vibl(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA5_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for VibL %d", error); + return error; + } + return error; +} +/** + * @brief Power up VIBR + * @channel_index Channel-index of VIBR + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_up_vibr(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if VibR PowerUp request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "VibR should have mono channel"); + return -EINVAL; + } + + /* Try to allocate vibrator for audio right channel */ + error = ste_audioio_vibrator_alloc(STE_AUDIOIO_CLIENT_AUDIO_R, + STE_AUDIOIO_CLIENT_AUDIO_R | STE_AUDIOIO_CLIENT_AUDIO_L); + if (error) { + dev_err(dev, " Couldn't allocate vibrator %d, client %d", + error, STE_AUDIOIO_CLIENT_AUDIO_R); + return error; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if VibR is already powered up */ + if (initialVal_DA & EN_DA6) + return 0; + + /* Enable DA6 for vibr */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, + SLOT13_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA5 from Slot 13 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE( + DIGITAL_DA_CHANNELS_ENABLE_REG, EN_DA6, 0); + if (0 != error) { + dev_err(dev, "Enable DA6 for VibR %d", error); + return error; + } + + /* Power Up VibR Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE( + ANALOG_OUTPUT_ENABLE_REG, EN_VIBR_MASK, 0); + if (0 != error) { + dev_err(dev, "Power Up VibR Class-D Driver %d", error); + return error; + } + + /* Power up VibR Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, + EN_VIBR_MASK, 0); + if (0 != error) { + dev_err(dev, + "Power up VibR Class D driver & digital path %d", + error); + return error; + } + return error; +} +/** + * @brief Power down VIBR + * @channel_index Channel-index of VIBR + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_vibr(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_DA = 0; + + /* Check if VibR PowerDown request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "VibR should have mono channel"); + return -EINVAL; + } + + initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if VibR is already powered down */ + if (!(initialVal_DA & EN_DA6)) + return 0; + + + /* Power Down VibR Class-D driver */ + error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0, + EN_VIBR_MASK); + if (0 != error) { + dev_err(dev, "Power Down VibR Class-D Driver %d", error); + return error; + } + + /* Power Down VibR Class D driver and digital path */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0, + EN_VIBR_MASK); + if (0 != error) { + dev_err(dev, + "Power Down VibR Class D driver & digital path %d", + error); + return error; + } + + /* Disable DA6 for VibR */ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG, + 0, EN_DA6); + if (0 != error) { + dev_err(dev, "Disable DA6 for VibR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0, + SLOT13_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, "Data sent to DA5 cleared from Slot 13 %d", + error); + return error; + } + + /* Release vibrator */ + ste_audioio_vibrator_release(STE_AUDIOIO_CLIENT_AUDIO_R); + + return error; +} +/** + * @brief Enable fading of VIBR + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_fade_vibr(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA6_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for VibR %d", error); + return error; + } + return error; +} +/** + * @brief Disable fading of VIBR + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_fade_vibr(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(DA6_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for VibR %d", error); + return error; + } + return error; +} +/** + * @brief Power up MIC1A + * @channel_index Channel-index of MIC1A + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_up_mic1a(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if Mic1 PowerUp request is mono channel */ + + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "MIC1 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + /* Check if Mic1 is already powered up or used by Dmic3 */ + if (EN_AD3 & initialVal_AD) + return 0; + + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT3); + if (0 != error) { + dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD3, 0); + if (0 != error) { + dev_err(dev, "Enable AD3 for Mic1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + SEL_DMIC3_FOR_AD_OUT3); + if (0 != error) { + dev_err(dev, "Select ADC1 for AD_OUT3 %d", error); + return error; + } + + /* Select MIC1A */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + SEL_MIC1B_CLR_MIC1A); + if (0 != error) { + dev_err(dev, "Select MIC1A %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC1, 0); + if (0 != error) { + dev_err(dev, "Power up Mic1 %d", error); + return error; + } + + /* Power Up ADC1 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC1, 0); + if (0 != error) { + dev_err(dev, "Power Up ADC1 %d", error); + return error; + } + +return error; +} +/** + * @brief Power down MIC1A + * @channel_index Channel-index of MIC1A + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_down_mic1a(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if Mic1 PowerDown request is mono channel */ + + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic1 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + /* Check if Mic1 is already powered down or used by Dmic3 */ + if (!(initialVal_AD & EN_AD3)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC1); + if (0 != error) { + dev_err(dev, "Power Down Mic1 %d", error); + return error; + } + + /* Power Down ADC1 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, POWER_UP_ADC1); + if (0 != error) { + dev_err(dev, "Power Down ADC1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD3); + if (0 != error) { + dev_err(dev, "Disable AD3 for Mic1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0, + DATA_FROM_AD_OUT3); + if (0 != error) { + dev_err(dev, "Slot 02 outputs data cleared from AD_OUT3 %d", + error); + return error; + } + return error; +} +/** + * @brief Mute MIC1A + * @channel_index Channel-index of MIC1A + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_mute_mic1a(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "MIC1 should have mono channel"); + return -EINVAL; + } + + /* Mute mic1 */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, MUT_MIC1, 0); + if (0 != error) { + dev_err(dev, "Mute Mic1 %d", error); + return error; + } + return error; +} +/** + * @brief Unmute MIC1A + * @channel_index Channel-index of MIC1A + * @gain Gain index of MIC1A + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_unmute_mic1a(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic1 should have mono channel"); + return -EINVAL; + } + /* UnMute mic1 */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, MUT_MIC1); + if (0 != error) { + dev_err(dev, "UnMute Mic1 %d", error); + return error; + } + return error; +} +/** + * @brief Enable fading of MIC1A + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_enable_fade_mic1a(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD3_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for Mic1 %d", error); + return error; + } + return error; +} +/** + * @brief Disable fading of MIC1A + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_disable_fade_mic1a(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD3_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for Mic1 %d", error); + return error; + } + return error; +} +/** + * @brief Power up MIC1B + * @channel_index Channel-index of MIC1B + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_mic1b(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error; + unsigned char initialVal_AD = 0; + + error = regulator_enable(regulator_avsource); + if (0 != error) { + dev_err(dev, "regulator avsource enable failed = %d", error); + return error; + } + /* GPIO35 settings to enable MIC 1B input instead of TVOUT */ + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR5_REG, + GPIO35_DIR_OUTPUT, 0); + if (0 != error) { + dev_err(dev, "setting AB8500_GPIO_DIR5_REG reg %d", error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_OUT5_REG, + GPIO35_DIR_OUTPUT, 0); + if (0 != error) { + dev_err(dev, "setting AB8500_GPIO_OUT5_REG reg %d", error); + return error; + } + + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic1 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + /* Check if Mic1 is already powered up or used by Dmic3 */ + if (EN_AD3 & initialVal_AD) + return 0; + + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT3); + if (0 != error) { + dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD3, 0); + if (0 != error) { + dev_err(dev, "Enable AD3 for Mic1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + SEL_DMIC3_FOR_AD_OUT3); + if (0 != error) { + dev_err(dev, "Select ADC1 for AD_OUT3 %d", error); + return error; + } + + /* Select MIC1B */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, SEL_MIC1B_CLR_MIC1A, + 0); + if (0 != error) { + dev_err(dev, "Select MIC1B %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC1, 0); + if (0 != error) { + dev_err(dev, "Power up Mic1 %d", error); + return error; + } + + /* Power Up ADC1 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC1, 0); + if (0 != error) { + dev_err(dev, "Power Up ADC1 %d", error); + return error; + } + return error; +} +/** + * @brief Power down MIC1B + * @channel_index Channel-index of MIC1B + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_mic1b(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error; + unsigned char initialVal_AD = 0; + + /* Check if Mic1 PowerDown request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic1 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if Mic1 is already powered down or used by Dmic3 */ + if (!(initialVal_AD & EN_AD3)) + return 0; + + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC1); + if (0 != error) { + dev_err(dev, "Power Down Mic1 %d", error); + return error; + } + + /* Power Down ADC1 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, POWER_UP_ADC1); + if (0 != error) { + dev_err(dev, "Power Down ADC1 %d", error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, 0, + EN_AD3); + if (0 != error) { + dev_err(dev, "Disable AD3 for Mic1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0, + DATA_FROM_AD_OUT3); + if (0 != error) { + dev_err(dev, "Slot 02 outputs data cleared from AD_OUT3 %d", + error); + return error; + } + + /* undo GPIO35 settings */ + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR5_REG, + 0, GPIO35_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "resetting AB8500_GPIO_DIR5_REG reg %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_OUT5_REG, + 0, GPIO35_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "resetting AB8500_GPIO_OUT5_REG reg %d", error); + return error; + } + + error = regulator_disable(regulator_avsource); + if (0 != error) { + dev_err(dev, "regulator avsource disable failed = %d", error); + return error; + } + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief enable hardware loop of mic1b + * @chnl_index Channel-index of MIC1B + * @hw_loop type of hardware loop + * @loop_gain gain value to be used in hardware loop + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + int loop_gain, struct device *dev, + void *cookie) +{ + int error; + struct transducer_context_t *trnsdr; + trnsdr = (struct transducer_context_t *)cookie; + + switch (hw_loop) { + /* Check if HSL is active */ + case AUDIOIO_SIDETONE_LOOP: + if (!(trnsdr[HS_CH].is_power_up[e_CHANNEL_1]) + && !(trnsdr[EAR_CH].is_power_up[e_CHANNEL_1])) { + error = -EFAULT; + dev_err(dev, + "HS or Earpiece not powered up error = %d", + error); + return error; + } + + /* For ch1, Power On STFIR1, data comes from AD3*/ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + FIR1_FROMAD3, 0); + if (error) + dev_err(dev, "FIR1 data comes from AD_OUT3 %d", + error); + error = HW_REG_WRITE(SIDETONE_FIR1_GAIN_REG, loop_gain); + if (error) { + dev_err(dev, + "Set FIR1 Gain index = %d", + error); + return error; + } + break; + default: + error = -EINVAL; + dev_err(dev, "loop not supported %d", error); + } + return error; +} + +/** + * @brief disable hardware loop of mic1b + * @chnl_index Channel-index of MIC1B + * @hw_loop type of hardware loop + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + struct device *dev, void *cookie) +{ + int error; + struct transducer_context_t *trnsdr; + trnsdr = (struct transducer_context_t *)cookie; + + switch (hw_loop) { + /* Check if HSL is active */ + case AUDIOIO_SIDETONE_LOOP: + if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1] + && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) { + error = -EFAULT; + dev_err(dev, "HS or Earpiece not powered up, err = %d", + error); + return error; + } + + /* For ch1, Power down STFIR1, data comes from AD3*/ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + 0, FIR1_FROMAD3); + if (error) { + dev_err(dev, "FIR1 data comes from AD_OUT3, err = %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG, + 0, FIR_FILTERCONTROL); + if (error) { + dev_err(dev, + "ST FIR Filters disable failed %d", error); + return error; + } + break; + default: + error = -EINVAL; + dev_err(dev, "loop not supported %d", error); + } + return error; +} + +/** + * @brief Power up MIC2 + * @channel_index Channel-index of MIC2 + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_mic2(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if Mic2 PowerUp request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic2 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if Mic2 is already powered up or used by LINR or Dmic2 */ + if (EN_AD2 & initialVal_AD) + return 0; + + + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT2); + if (0 != error) { + dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, EN_AD2, + 0); + if (0 != error) { + dev_err(dev, "Enable AD2 for Mic2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + SEL_DMIC2_FOR_AD_OUT2); + if (0 != error) { + dev_err(dev, "Select ADC2 for AD_OUT2 %d", error); + return error; + } + + /* Select mic2 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + SEL_LINR_CLR_MIC2); + if (0 != error) { + dev_err(dev, "Select MIC2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC2, 0); + if (0 != error) { + dev_err(dev, "Power up Mic2 %d", error); + return error; + } + + /* Power Up ADC1 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC2, 0); + if (0 != error) { + dev_err(dev, "Power Up ADC2 %d", error); + return error; + } + return error; +} +/** + * @brief Power down MIC2 + * @channel_index Channel-index of MIC2 + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_power_down_mic2(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if Mic2 PowerDown request is mono channel */ + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic2 should have mono channel"); + return -EINVAL; + } + + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + + /* Check if Mic2 is already powered down or used by LINR or Dmic2 */ + if (!(initialVal_AD & EN_AD2)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC2); + if (0 != error) { + dev_err(dev, "Power Down Mic2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD2); + if (0 != error) { + dev_err(dev, "Disable AD2 for Mic2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0, + (DATA_FROM_AD_OUT2<<4)); + if (0 != error) { + dev_err(dev, "Slot 01 outputs data cleared from AD_OUT2 %d", + error); + return error; + } + return error; +} +/** + * @brief Mute MIC2 + * @channel_index Channel-index of MIC2 + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_mute_mic2(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic2 should have mono channel"); + return -EINVAL; + } + + /* Mute mic2 */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, MUT_MIC2, 0); + if (0 != error) { + dev_err(dev, "Mute Mic2 %d", error); + return error; + } + return error; +} +/** + * @brief Unmute MIC2 + * @channel_index Channel-index of MIC2 + * @gain Gain index of MIC2 + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_unmute_mic2(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + if (!(channel_index & e_CHANNEL_1)) { + dev_err(dev, "Mic2 should have mono channel"); + return -EINVAL; + } + /* UnMute mic2 */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, MUT_MIC2); + if (0 != error) { + dev_err(dev, "UnMute Mic2 %d", error); + return error; + } + return error; +} +/** + * @brief Enable fading of MIC2 + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_enable_fade_mic2(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for Mic2 %d", error); + return error; + } + return error; +} +/** + * @brief Disable fading of MIC2 + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_fade_mic2(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for Mic2 %d", error); + return error; + } + + return error; +} +/** + * @brief Power up LinIn + * @channel_index Channel-index of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_lin(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if LinIn PowerUp request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "LinIn should have mono or stereo channels"); + return -EINVAL; + } + + /* Enable AD1 for LinInL */ + if (channel_index & e_CHANNEL_1) { + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (initialVal_AD & EN_AD1) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, + DATA_FROM_AD_OUT1, 0); + if (0 != error) { + dev_err(dev, "Slot 00 outputs data from AD_OUT1 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD1, 0); + if (0 != error) { + dev_err(dev, "Enable AD1 for LinInL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + SEL_DMIC1_FOR_AD_OUT1); + if (0 != error) { + dev_err(dev, "Select ADC3 for AD_OUT1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE( + LINE_IN_MIC_CONF_REG, EN_LIN_IN_L, 0); + if (0 != error) { + dev_err(dev, "Power up LinInL %d", error); + return error; + } + + /* Power Up ADC3 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, + POWER_UP_ADC3, 0); + if (0 != error) { + dev_err(dev, "Power Up ADC3 %d", error); + return error; + } + } + /* Enable AD2 for LinInR */ + + if (channel_index & e_CHANNEL_2) { + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (EN_AD2 & initialVal_AD) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, + (DATA_FROM_AD_OUT2<<4), 0); + if (0 != error) { + dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD2, 0); + if (0 != error) { + dev_err(dev, "Enable AD2 LinInR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, + SEL_DMIC2_FOR_AD_OUT2); + if (0 != error) { + dev_err(dev, "Select ADC2 for AD_OUT2 %d", error); + return error; + } + + /* Select LinInR */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, + SEL_LINR_CLR_MIC2, 0); + if (0 != error) { + dev_err(dev, "Select LinInR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, + EN_LIN_IN_R, 0); + if (0 != error) { + dev_err(dev, "Power up LinInR %d", error); + return error; + } + + /* Power Up ADC2 */ + error = HW_ACODEC_MODIFY_WRITE( + ADC_DAC_ENABLE_REG, POWER_UP_ADC2, 0); + if (0 != error) { + dev_err(dev, "Power Up ADC2 %d", error); + return error; + } + } + return error; +} +/** + * @brief Power down LinIn + * @channel_index Channel-index of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_down_lin(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if LinIn PowerDown request is mono or Stereo channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "LinIn should have mono or stereo channels"); + return -EINVAL; + } + + /* Enable AD1 for LinInL */ + if (channel_index & e_CHANNEL_1) { + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD1)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, + EN_LIN_IN_L); + if (0 != error) { + dev_err(dev, "Power Down LinInL %d", error); + return error; + } + + /* Power Down ADC3 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + POWER_UP_ADC3); + if (0 != error) { + dev_err(dev, "Power Down ADC3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD1); + if (0 != error) { + dev_err(dev, "Disable AD1 for LinInL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0, + DATA_FROM_AD_OUT1); + if (0 != error) { + dev_err(dev, + "Slot 00 outputs data cleared from AD_OUT1 %d", + error); + return error; + } + } + + /* Enable AD2 for LinInR */ + if (channel_index & e_CHANNEL_2) { + initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD2)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, + EN_LIN_IN_R); + if (0 != error) { + dev_err(dev, "Power Down LinInR %d", error); + return error; + } + + /* Power Down ADC2 */ + error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, + POWER_UP_ADC2); + if (0 != error) { + dev_err(dev, "Power Down ADC2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD2); + if (0 != error) { + dev_err(dev, "Disable AD2 LinInR %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0, + (DATA_FROM_AD_OUT2<<4)); + if (0 != error) { + dev_err(dev, + "Slot01 outputs data cleared from AD_OUT2 %d", + error); + return error; + } + } + return error; +} +/** + * @brief Mute LinIn + * @channel_index Channel-index of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_mute_lin(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "LinIn should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + /* Mute LinInL */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, + MUT_LIN_IN_L, 0); + if (0 != error) { + dev_err(dev, "Mute LinInL %d", error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* Mute LinInR */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, + MUT_LIN_IN_R, + 0); + if (0 != error) { + dev_err(dev, "Mute LinInR %d", error); + return error; + } + } + return error; +} +/** + * @brief Unmute LinIn + * @channel_index Channel-index of LinIn + * @gain Gain index of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_unmute_lin(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "LinIn should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + /* UnMute LinInL */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, + MUT_LIN_IN_L); + if (0 != error) { + dev_err(dev, "UnMute LinInL %d", error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* UnMute LinInR */ + error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, + MUT_LIN_IN_R); + if (0 != error) { + dev_err(dev, "UnMute LinInR %d", error); + return error; + } + } + return error; +} +/** + * @brief Enables fading of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_enable_fade_lin(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD1_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for LinInL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, 0, DIS_FADING); + if (0 != error) { + dev_err(dev, "Enable fading for LinInR %d", error); + return error; + } + return error; +} +/** + * @brief Disables fading of LinIn + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_disable_fade_lin(struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(AD1_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for LinInL %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, DIS_FADING, 0); + if (0 != error) { + dev_err(dev, "Disable fading for LinInR %d", error); + return error; + } + return error; +} +/** + * @brief Power Up DMIC12 LinIn + * @channel_index Channel-index of DMIC12 + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_power_up_dmic12(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if DMic12 request is mono or Stereo */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "DMic12 does not support more than 2 channels"); + + return -EINVAL; + } + + /* Setting Direction for GPIO pins on AB8500 */ + error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, GPIO27_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "Setting Direction for GPIO pins on AB8500 %d", + error); + return error; + } + + /* Enable AD1 for Dmic1 */ + if (channel_index & e_CHANNEL_1) { + /* Check if DMIC1 is already powered up or used by LinInL */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (initialVal_AD & EN_AD1) + return 0; + + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, + DATA_FROM_AD_OUT1); + if (0 != error) { + dev_err(dev, "Slot 00 outputs data from AD_OUT1 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD1, 0); + if (0 != error) { + dev_err(dev, "Enable AD1 for DMIC1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + SEL_DMIC1_FOR_AD_OUT1, 0); + if (0 != error) { + dev_err(dev, "Select DMIC1 for AD_OUT1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC1, 0); + if (0 != error) { + dev_err(dev, "Enable DMIC1 %d", error); + return error; + } + } + /* Enable AD2 for Dmic2 */ + + if (channel_index & e_CHANNEL_2) { + /* Check if DMIC2 is already powered up + or used by Mic2 or LinInR */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (initialVal_AD & EN_AD2) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, + (DATA_FROM_AD_OUT2<<4), 0); + if (0 != error) { + dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD2, 0); + if (0 != error) { + dev_err(dev, "Enable AD2 for DMIC2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + SEL_DMIC2_FOR_AD_OUT2, 0); + if (0 != error) { + dev_err(dev, "Select DMIC2 for AD_OUT2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC2, 0); + if (0 != error) { + dev_err(dev, "Enable DMIC2 %d", error); + return error; + } + } + + return error; +} +/** + * @brief Power down DMIC12 LinIn + * @channel_index Channel-index of DMIC12 + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_power_down_dmic12(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if DMic12 request is mono or Stereo or multi channel */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "DMic12 does not support more than 2 channels"); + + return -EINVAL; + } + + /* Setting Direction for GPIO pins on AB8500 */ + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR4_REG, 0, + GPIO27_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "Clearing Direction for GPIO pins on AB8500 %d", + error); + return error; + } + /* Enable AD1 for Dmic1 */ + if (channel_index & e_CHANNEL_1) { + /* Check if DMIC1 is already powered Down or used by LinInL */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD1)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC1); + if (0 != error) { + dev_err(dev, "Enable DMIC1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD1); + if (0 != error) { + dev_err(dev, "Disable AD1 for DMIC1 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0, + DATA_FROM_AD_OUT1); + if (0 != error) { + dev_err(dev, + "Slot 00 outputs data cleared from AD_OUT1 %d", + error); + return error; + } + } + + /* Enable AD2 for Dmic2 */ + if (channel_index & e_CHANNEL_2) { + /* MIC2 is already powered Down or used by Mic2 or LinInR */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD2)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC2); + if (0 != error) { + dev_err(dev, "Enable DMIC2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD2); + if (0 != error) { + dev_err(dev, "Disable AD2 for DMIC2 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0, + (DATA_FROM_AD_OUT2<<4)); + if (0 != error) { + dev_err(dev, + "Slot 01 outputs data cleared from AD_OUT2 %d", + error); + return error; + } + } + return error; +} +/** + * @brief Get headset gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_get_headset_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + int i = 0; + if (gain_index == 0) { + + *left_volume = 0 - HW_REG_READ(DA1_DIGITAL_GAIN_REG); + *right_volume = 0 - HW_REG_READ(DA2_DIGITAL_GAIN_REG); + + } + + if (gain_index == 1) { + *left_volume = 8 - HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG); + *right_volume = 8 - HW_REG_READ(HSR_DIGITAL_GAIN_REG); + } + + if (gain_index == 2) { + i = (HW_REG_READ(ANALOG_HS_GAIN_REG)>>4); + *left_volume = hs_analog_gain_table[i]; + i = (HW_REG_READ(ANALOG_HS_GAIN_REG) & MASK_QUARTET0); + *right_volume = hs_analog_gain_table[i]; + } + return 0; +} +/** + * @brief Get earpiece gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_get_earpiece_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + if (0 == gain_index) + *left_volume = 0 - HW_REG_READ(DA1_DIGITAL_GAIN_REG); + if (1 == gain_index) + *left_volume = 8 - HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG); + return 0; +} +/** + * @brief Get ihf gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_get_ihf_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + + *left_volume = 0 - HW_REG_READ(DA3_DIGITAL_GAIN_REG); + *right_volume = 0 - HW_REG_READ(DA4_DIGITAL_GAIN_REG); + return 0; +} +/** + * @brief Get vibl gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_get_vibl_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + + *left_volume = 0 - HW_REG_READ(DA5_DIGITAL_GAIN_REG); + + return 0; +} +/** + * @brief Get vibr gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_get_vibr_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + + *right_volume = 0 - HW_REG_READ(DA6_DIGITAL_GAIN_REG); + return 0; +} +/** + * @brief Get MIC1A & MIC2A gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_get_mic1a_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + if (gain_index == 0) + *left_volume = 31 - HW_REG_READ(AD3_DIGITAL_GAIN_REG); + if (gain_index == 1) + *left_volume = HW_REG_READ(ANALOG_MIC1_GAIN_REG); + + return 0; +} +/** + * @brief Get MIC2 gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_get_mic2_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + if (gain_index == 0) + *left_volume = 31 - HW_REG_READ(AD2_DIGITAL_GAIN_REG); + if (gain_index == 1) + *left_volume = HW_REG_READ(ANALOG_MIC2_GAIN_REG); + + return 0; +} +/** + * @brief Get Lin IN gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_get_lin_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + if (gain_index == 0) { + *left_volume = 31 - HW_REG_READ(AD1_DIGITAL_GAIN_REG); + *right_volume = 31 - HW_REG_READ(AD2_DIGITAL_GAIN_REG); + } + + if (gain_index == 0) { + *left_volume = 2 * ((HW_REG_READ(ANALOG_HS_GAIN_REG)>>4) - 5); + *right_volume = 2 * (HW_REG_READ(ANALOG_LINE_IN_GAIN_REG) - 5); + } + + return 0; +} +/** + * @brief Get DMIC12 gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_get_dmic12_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + + *left_volume = HW_REG_READ(AD1_DIGITAL_GAIN_REG); + + *right_volume = HW_REG_READ(AD2_DIGITAL_GAIN_REG); + + return 0; +} +/** + * @brief Get DMIC34 gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_get_dmic34_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + *left_volume = HW_REG_READ(AD3_DIGITAL_GAIN_REG); + *right_volume = HW_REG_READ(AD4_DIGITAL_GAIN_REG); + + return 0; +} +/** + * @brief Get DMIC56 gain + * @left_volume + * @right_volume + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_get_dmic56_gain(int *left_volume, int *right_volume, + u16 gain_index, struct device *dev) +{ + *left_volume = HW_REG_READ(AD5_DIGITAL_GAIN_REG); + + *right_volume = HW_REG_READ(AD6_DIGITAL_GAIN_REG); + return 0; +} +/** + * @brief Set gain of headset along a specified channel + * @channel_index Channel-index of headset + * @gain_index Gain index of headset + * @gain_value Gain value of headset + * @linear + * @return 0 on success otherwise negative error code + */ + + +int ste_audio_io_set_headset_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + int i = 0; + int acodec_device_id; + + acodec_device_id = abx500_get_chip_id(dev); + + if (channel_index & e_CHANNEL_1) { + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA1_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(DA1_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSL gainindex = %d %d", + gain_index, error); + return error; + } + } + + if (gain_index == 1) { + int gain = 0; + gain = 8 - gain_value; + + initial_val = HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(HSL_EAR_DIGITAL_GAIN_REG, + ((initial_val & (~HS_DIGITAL_GAIN_MASK)) | (gain & + HS_DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSL gain index = %d %d", + gain_index, error); + return error; + } + } + + if (gain_index == 2) { + /* Set Analog gain */ + int gain = -1; + + if (gain_value % 2) { + gain_value -= 1; + dev_err(dev, + "Odd Gain received.Fixing it to 2dB step gain_value = %d", + gain_value); + } + /* Fix for 4dB step gains. Select one lower value */ + if (gain_value == -22) + gain_value = -24; + + if (gain_value == -26) + gain_value = -28; + + if (gain_value == -30) + gain_value = -32; + + for (i = 0 ; i < 16; i++) { + if (hs_analog_gain_table[i] == gain_value) { + gain = i<<4; + break; + } + } + if (gain == -1) + return -1; + + if ((AB8500_REV_10 == acodec_device_id) || + (AB8500_REV_11 == acodec_device_id)) { + if (!gain) + gain = 0x10; + gain = 0xF0 - gain; + } + initial_val = HW_REG_READ(ANALOG_HS_GAIN_REG); + + /* Write gain */ + error = HW_REG_WRITE(ANALOG_HS_GAIN_REG, ((initial_val & + (~L_ANALOG_GAIN_MASK)) | (gain & L_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSL gain index = %d %d", + gain_index, error); + return error; + } + } + } + + /* for HSR */ + if (channel_index & e_CHANNEL_2) { + /* Set Gain HSR */ + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA2_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(DA2_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSR gain index = %d %d", + gain_index, error); + return error; + } + } + + if (gain_index == 1) { + int gain = 0; + gain = 8 - gain_value; + + initial_val = HW_REG_READ(HSR_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(HSR_DIGITAL_GAIN_REG, ((initial_val + & (~HS_DIGITAL_GAIN_MASK)) | (gain & + HS_DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSR gain index = %d %d", + gain_index, error); + return error; + } + + } + + if (gain_index == 2) { + /* Set Analog gain */ + int gain = -1; + + if (gain_value % 2) { + gain_value -= 1; + dev_err(dev, + "Odd Gain received.Fixing it to 2dB step gain_value = %d", + gain_value); + } + /* Fix for 4dB step gains. Select one lower value */ + if (gain_value == -22) + gain_value = -24; + + if (gain_value == -26) + gain_value = -28; + + if (gain_value == -30) + gain_value = -32; + + for (i = 0 ; i < 16 ; i++) { + if (hs_analog_gain_table[i] == gain_value) { + gain = i; + break; + } + } + if (gain == -1) + return -1; + + if ((AB8500_REV_10 == acodec_device_id) || + (AB8500_REV_11 == acodec_device_id)) { + if (!gain) + gain = 1; + gain = 0x0F - gain; + } + initial_val = HW_REG_READ(ANALOG_HS_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(ANALOG_HS_GAIN_REG, ((initial_val & + (~R_ANALOG_GAIN_MASK)) | (gain & R_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain HSR gainindex = %d %d", + gain_index, error); + return error; + } + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Set gain of earpiece + * @channel_index Channel-index of earpiece + * @gain_index Gain index of earpiece + * @gain_value Gain value of earpiece + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_earpiece_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + if (channel_index & e_CHANNEL_1) { + if (0 == gain_index) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA1_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(DA1_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Ear gainindex = %d %d", + gain_index, error); + return error; + } + } + + if (gain_index == 1) { + int gain = 0; + gain = 8 - gain_value; + + initial_val = HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(HSL_EAR_DIGITAL_GAIN_REG, + ((initial_val & (~HS_DIGITAL_GAIN_MASK)) | (gain & + HS_DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Ear gainindex = %d %d", + gain_index, error); + return error; + } + } + } + dump_acodec_registers(__func__, dev); + return error; +} +/** + * @brief Set gain of vibl + * @channel_index Channel-index of vibl + * @gain_index Gain index of vibl + * @gain_value Gain value of vibl + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_vibl_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain vibl */ + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA5_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(DA5_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain VibL gain index = %d %d", + gain_index, error); + return error; + } + } + } + return error; +} +/** + * @brief Set gain of vibr + * @channel_index Channel-index of vibr + * @gain_index Gain index of vibr + * @gain_value Gain value of vibr + * @linear + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_set_vibr_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, + u32 linear, + struct device *dev) +{ + + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain vibr */ + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA6_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(DA6_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain VibR gain index = %d %d", + gain_index, error); + return error; + } + } + } + return error; +} +/** + * @brief Set gain of ihf along a specified channel + * @channel_index Channel-index of ihf + * @gain_index Gain index of ihf + * @gain_value Gain value of ihf + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_ihf_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain IHFL */ + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA3_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(DA3_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain IHFL gain index = %d %d", + gain_index, error); + return error; + } + + } + } + if (channel_index & e_CHANNEL_2) { + /* Set Gain IHFR */ + if (gain_index == 0) { + int gain = 0; + gain = 0 - gain_value; + + initial_val = HW_REG_READ(DA4_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(DA4_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain IHFR gain index = %d %d", + gain_index, error); + return error; + } + } + } + + return error; +} +/** + * @brief Set gain of MIC1A & MIC1B + * @channel_index Channel-index of MIC1 + * @gain_index Gain index of MIC1 + * @gain_value Gain value of MIC1 + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_mic1a_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain mic1 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD3_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(AD3_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Mic1 gain index = %d %d", + gain_index, error); + return error; + } + + } + + if (gain_index == 1) { + /* Set Analog gain */ + initial_val = HW_REG_READ(ANALOG_MIC1_GAIN_REG); + + /* Write gain */ + error = HW_REG_WRITE(ANALOG_MIC1_GAIN_REG, ((initial_val + & (~MIC_ANALOG_GAIN_MASK)) | (gain_value & + MIC_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Mic1 gain index = %d %d", + gain_index, error); + return error; + } + } + } + return error; +} +/** + * @brief Set gain of MIC2 + * @channel_index Channel-index of MIC2 + * @gain_index Gain index of MIC2 + * @gain_value Gain value of MIC2 + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_mic2_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, + u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain mic2 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG, ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Mic2 gain index = %d %d", + gain_index, error); + return error; + } + + } + + if (gain_index == 1) { + /* Set Analog gain */ + initial_val = HW_REG_READ(ANALOG_MIC2_GAIN_REG); + + /* Write gain */ + error = HW_REG_WRITE(ANALOG_MIC2_GAIN_REG, ((initial_val + & (~MIC_ANALOG_GAIN_MASK)) | (gain_value & + MIC_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain Mic2 gain index = %d %d", + gain_index, error); + return error; + } + } + } + return error; +} +/** + * @brief Set gain of Lin IN along a specified channel + * @channel_index Channel-index of Lin In + * @gain_index Gain index of Lin In + * @gain_value Gain value of Lin In + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_lin_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD1_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(AD1_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain LinInL gain index = %d %d", + gain_index, error); + return error; + } + + } + + if (gain_index == 1) { + int gain = 0; + /* + * Converting -10 to 20 range into 0 - 15 + * & shifting it left by 4 bits + */ + gain = ((gain_value/2) + 5)<<4; + + initial_val = HW_REG_READ(ANALOG_LINE_IN_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(ANALOG_LINE_IN_GAIN_REG, + ((initial_val & (~L_ANALOG_GAIN_MASK)) | (gain & + L_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain LinInL gain index = %d %d", + gain_index, error); + return error; + } + } + } + + if (channel_index & e_CHANNEL_2) { + /* Set Gain LinInR */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain LinInR gain index = %d%d", + gain_index, error); + return error; + } + } + if (gain_index == 1) { + int gain = 0; + /* Converting -10 to 20 range into 0 - 15 */ + gain = ((gain_value/2) + 5); + + initial_val = HW_REG_READ(ANALOG_LINE_IN_GAIN_REG); + /* Write gain */ + error = HW_REG_WRITE(ANALOG_LINE_IN_GAIN_REG, + ((initial_val & (~R_ANALOG_GAIN_MASK)) | (gain & + R_ANALOG_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain LinInR gain index = %d %d", + gain_index, error); + return error; + } + } + } + + return error; +} +/** + * @brief Set gain of DMIC12 along a specified channel + * @channel_index Channel-index of DMIC12 + * @gain_index Gain index of DMIC12 + * @gain_value Gain value of DMIC12 + * @linear + * @return 0 on success otherwise negative error code + */ + +int ste_audio_io_set_dmic12_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain Dmic1 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD1_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(AD1_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain DMic1 gain index = %d %d", + gain_index, error); + return error; + } + } + } + if (channel_index & e_CHANNEL_2) { + /* Set Gain Dmic2 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain DMic2 gain index = %d %d", + gain_index, error); + return error; + } + } + } + return error; +} + +int ste_audio_io_switch_to_burst_mode_headset(int burst_fifo_switch_frame, + struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_INT_CONTROL_REG, + WAKEUP_SIGNAL_SAMPLE_COUNT, 0); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_LENGTH_REG, + BURST_FIFO_TRANSFER_LENGTH, 0); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_CONTROL_REG, + (BURST_FIFO_INF_RUNNING | BURST_FIFO_INF_IN_MASTER_MODE + |PRE_BIT_CLK0_COUNT), 0); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_WAKE_UP_DELAY_REG, + BURST_FIFO_WAKUP_DEALAY, 0); + if (0 != error) + return error; + + error = HW_REG_WRITE(BURST_FIFO_SWITCH_FRAME_REG, + burst_fifo_switch_frame); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, + IF0_BFifoEn, 0); + if (0 != error) + return error; + + return error; +} +int ste_audio_io_switch_to_normal_mode_headset( + struct device *dev) +{ + int error = 0; + + error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, 0, + IF0_BFifoEn); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_INT_CONTROL_REG, + 0, WAKEUP_SIGNAL_SAMPLE_COUNT); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_LENGTH_REG, + 0, BURST_FIFO_TRANSFER_LENGTH); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_CONTROL_REG, 0, + (BURST_FIFO_INF_RUNNING | BURST_FIFO_INF_IN_MASTER_MODE + |PRE_BIT_CLK0_COUNT)); + if (0 != error) + return error; + + error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_WAKE_UP_DELAY_REG, + 0, BURST_FIFO_WAKUP_DEALAY); + if (0 != error) + return error; + + return error; +} + + +int ste_audio_io_mute_vibl(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + return 0; +} + +int ste_audio_io_unmute_vibl(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + return 0; +} + +int ste_audio_io_mute_vibr(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + return 0; +} +int ste_audio_io_unmute_vibr(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + return 0; +} + +int ste_audio_io_mute_dmic12(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + error = ste_audio_io_set_dmic12_gain(channel_index, 0, -32, + 0, dev); + if (0 != error) { + dev_err(dev, "Mute dmic12 %d", error); + return error; + } + } + + return error; + +} + +int ste_audio_io_unmute_dmic12(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + error = ste_audio_io_set_dmic12_gain(channel_index, + 0, gain[0], 0, dev); + if (0 != error) { + dev_err(dev, "UnMute dmic12 %d", error); + return error; + } + } + return error; +} +int ste_audio_io_enable_fade_dmic12(struct device *dev) +{ + return 0; +} + +int ste_audio_io_disable_fade_dmic12(struct device *dev) +{ + return 0; +} + +/** + * @brief enable hardware loop of dmic12 + * @chnl_index Channel-index of dmic12 + * @hw_loop type of hardware loop + * @loop_gain gain value to be used in hardware loop + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_enable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + int loop_gain, struct device *dev, + void *cookie) +{ + int error = 0; + struct transducer_context_t *trnsdr; + trnsdr = (struct transducer_context_t *)cookie; + + switch (hw_loop) { + /* Check if HSL is active */ + case AUDIOIO_SIDETONE_LOOP: + if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1] + && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) { + error = -EFAULT; + dev_err(dev, + "Sidetone enable needs HS or Earpiece powered up, err = %d", + error); + return error; + } + + if (chnl_index & e_CHANNEL_1) { + /* For ch1, Power On STFIR1, data comes from AD1*/ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + 0, FIR1_FROMAD1); + if (error) { + dev_err(dev, "FIR1 data comes from AD_OUT1 %d", + error); + return error; + } + + error = HW_REG_WRITE(SIDETONE_FIR1_GAIN_REG, loop_gain); + if (error) { + dev_err(dev, + "Set FIR1 Gain index = %d", error); + return error; + } + } + + if (chnl_index & e_CHANNEL_2) { + /* For ch2, Power On STFIR1, data comes from AD2*/ + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + 0, FIR1_FROMAD2); + if (error) { + dev_err(dev, "FIR1 data comes from AD_OUT2 %d", + error); + return error; + } + error = HW_REG_WRITE(SIDETONE_FIR2_GAIN_REG, loop_gain); + if (error) { + dev_err(dev, + "Set FIR2 Gain error = %d", error); + return error; + } + } + break; + default: + error = -EINVAL; + dev_err(dev, "loop not supported %d", error); + } + dump_acodec_registers(__func__, dev); + return error; +} + +/** + * @brief disable hardware loop of dmic12 + * @chnl_index Channel-index of dmic12 + * @hw_loop type of hardware loop + * @return 0 on success otherwise negative error code + */ +int ste_audio_io_disable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + struct device *dev, void *cookie) +{ + int error = -EINVAL; + struct transducer_context_t *trnsdr; + trnsdr = (struct transducer_context_t *)cookie; + + switch (hw_loop) { + /* Check if HSL is active */ + case AUDIOIO_SIDETONE_LOOP: + if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1] + && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) { + error = -EFAULT; + dev_err(dev, + "Sidetone disable needs HS or Earpiece powered up, err = %d", + error); + return error; + } + + if (chnl_index & e_CHANNEL_1) { + /* For ch1, Power On STFIR1, data comes from AD1*/ + error = + HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + 0, FIR1_FROMAD1); + if (error) + dev_err(dev, "FIR1 data comes from AD_OUT1 %d", + error); + } + + if (chnl_index & e_CHANNEL_2) { + /* For ch2, Power On STFIR1, data comes from AD2*/ + error = + HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2, + 0, FIR1_FROMAD2); + if (error) + dev_err(dev, "FIR1 data comes from AD_OUT2 %d", + error); + } + error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG, + 0, FIR_FILTERCONTROL); + if (error) { + dev_err(dev, + "ST FIR Filters disable failed %d", error); + return error; + } + break; + default: + dev_err(dev, "loop not supported %d", error); + } + dump_acodec_registers(__func__, dev); + return error; +} + +int ste_audio_io_power_up_dmic34(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + /* Check if DMic34 request is mono or Stereo */ + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "DMic34 does not support more than 2 channels"); + return -EINVAL; + } + + /* Setting Direction for GPIO pins on AB8500 */ + error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, GPIO29_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "Setting Direction for GPIO pins on AB8500 %d", + error); + return error; + } + + if (channel_index & e_CHANNEL_1) { + /* Check if DMIC3 is already powered up or used by Mic1A + or Mic1B */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + + if (initialVal_AD & (EN_AD3)) + return 0; + + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, + DATA_FROM_AD_OUT3, 0); + if (0 != error) { + dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, EN_AD3, + 0); + if (0 != error) { + dev_err(dev, "Enable AD3 for DMIC3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, + SEL_DMIC3_FOR_AD_OUT3, + 0); + if (0 != error) { + dev_err(dev, "Select DMIC3 for AD_OUT3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC3, 0); + if (0 != error) { + dev_err(dev, "Enable DMIC3 %d", error); + return error; + } +} + + /* Enable AD4 for Dmic4 */ + if (channel_index & e_CHANNEL_2) { + /* Check if DMIC4 is already powered up */ + if (initialVal_AD & (EN_AD4)) + return 0; + + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, + (DATA_FROM_AD_OUT4<<4), 0); + if (0 != error) { + dev_err(dev, "Slot 03 outputs data from AD_OUT4 %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + EN_AD4, 0); + if (0 != error) { + dev_err(dev, "Enable AD4 for DMIC4 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC4, 0); + if (0 != error) { + dev_err(dev, "Enable DMIC4 %d", error); + return error; + } + } + return error; +} + +int ste_audio_io_power_down_dmic34(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "DMic34 does not support more than 2 channels"); + return -EINVAL; + } + + /* Setting Direction for GPIO pins on AB8500 */ + error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR4_REG, 0, + GPIO29_DIR_OUTPUT); + if (0 != error) { + dev_err(dev, "Clearing Direction for GPIO pins on AB8500 %d", + error); + return error; + } + + /* Enable AD1 for Dmic1 */ + if (channel_index & e_CHANNEL_1) { + /* Check if DMIC3 is already powered Down or used by Mic1A + or Mic1B */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD3)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC3); + if (0 != error) { + dev_err(dev, "Enable DMIC3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, + EN_AD3); + if (0 != error) { + dev_err(dev, "Disable AD3 for DMIC3 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0, + DATA_FROM_AD_OUT3); + if (0 != error) { + dev_err(dev, + "Slot 02 outputs data cleared from AD_OUT3 %d", + error); + return error; + } + } + + /* Enable AD4 for Dmic4 */ + if (channel_index & e_CHANNEL_2) { + /* Check if DMIC4 is already powered down */ + initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG); + if (!(initialVal_AD & EN_AD4)) + return 0; + + error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC4); + if (0 != error) { + dev_err(dev, "Enable DMIC4 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, + 0, EN_AD4); + if (0 != error) { + dev_err(dev, "Disable AD4 for DMIC4 %d", error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0, + (DATA_FROM_AD_OUT4<<4)); + if (0 != error) { + dev_err(dev, + "Slot 03 outputs data cleared from AD_OUT4 %d", + error); + return error; + } + } + return error; +} +int ste_audio_io_set_dmic34_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + int error = 0; + unsigned char initial_val = 0; + + if (channel_index & e_CHANNEL_1) { + /* Set Gain Dmic3 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD3_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(AD3_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + if (0 != error) { + dev_err(dev, + "Set Gain DMic3 gain index = %d %d", + gain_index, error); + return error; + } + } + } + + if (channel_index & e_CHANNEL_2) { + /* Set Gain Dmic4 */ + if (gain_index == 0) { + int gain = 0; + gain = 31 - gain_value; + + initial_val = HW_REG_READ(AD4_DIGITAL_GAIN_REG); + error = HW_REG_WRITE(AD4_DIGITAL_GAIN_REG, + ((initial_val + & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK))); + + if (0 != error) { + dev_err(dev, + "Set Gain DMic4 gain index = %d %d", + gain_index, error); + return error; + } + } + } + + return error; +} +int ste_audio_io_mute_dmic34(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + error = ste_audio_io_set_dmic34_gain(channel_index, 0, -32, + 0, dev); + if (0 != error) { + dev_err(dev, "Mute dmic34 %d", error); + return error; + } + } + return error; +} +int ste_audio_io_unmute_dmic34(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + int error = 0; + if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + error = ste_audio_io_set_dmic34_gain(channel_index, + 0, gain[0], 0, dev); + if (0 != error) { + dev_err(dev, "UnMute dmic34 %d", error); + return error; + } + } + return error; +} +int ste_audio_io_enable_fade_dmic34(struct device *dev) +{ + return 0; +} + +int ste_audio_io_disable_fade_dmic34(struct device *dev) +{ + return 0; +} + +int ste_audio_io_power_up_dmic56(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + return 0; +} +int ste_audio_io_power_down_dmic56(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + return 0; +} +int ste_audio_io_set_dmic56_gain(enum AUDIOIO_CH_INDEX channel_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev) +{ + return 0; +} +int ste_audio_io_mute_dmic56(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + return 0; +} +int ste_audio_io_unmute_dmic56(enum AUDIOIO_CH_INDEX channel_index, int *gain, + struct device *dev) +{ + return 0; +} +int ste_audio_io_enable_fade_dmic56(struct device *dev) +{ + return 0; +} + +int ste_audio_io_disable_fade_dmic56(struct device *dev) +{ + return 0; +} + +int ste_audio_io_configure_if1(struct device *dev) +{ + int error = 0; + + error = HW_REG_WRITE(IF1_CONF_REG, IF_DELAYED | + I2S_LEFT_ALIGNED_FORMAT | WORD_LENGTH_16); + if (error != 0) { + dev_err(dev, + "Configure IF1: I2S Format 16 Bits word length error = %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, IF1_MASTER, 0); + if (error != 0) { + dev_err(dev, + "Configure IF1: IF1 master error = %d", + error); + return error; + } + + error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, + EN_FSYNC_BITCLK1, 0); + if (error != 0) { + dev_err(dev, + "ConfigIF1 bitclk is 32x48KHz, enable Fsync1 and Bitclk1 error = %d", + error); + return error; + } + return error; +} + +int ste_audio_io_power_up_fmrx(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal = 0; + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "FMRX should have mono or stereo channels"); + return -EINVAL; + } + + ste_audio_io_configure_if1(dev); + + if (channel_index & e_CHANNEL_1) { + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, + SLOT24_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA_IN7 from Slot 24 %d", + error); + return error; + } + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, + SEL_AD_OUT8_FROM_DAIN7, 0); + if (0 != error) { + dev_err(dev, "Data sent to AD_OUT5 from DA_IN7 %d", + error); + return error; + } + + initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT6_7_REG); + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, + ((initialVal & MASK_QUARTET1)|SEL_IF6_FROM_AD_OUT5)); + if (0 != error) { + dev_err(dev, "Data sent to IF slot 6 from AD_OUT5 %d", + error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, + SLOT25_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA_IN8 from Slot 25 %d", + error); + return error; + } + + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, + SEL_AD_OUT6_FROM_DAIN8, 0); + if (0 != error) { + dev_err(dev, "Data sent to AD_OUT6 from DA_IN8 %d", + error); + return error; + } + + initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT6_7_REG); + + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, + (initialVal & MASK_QUARTET0)|SEL_IF7_FROM_AD_OUT6); + /* 5x is written */ + if (0 != error) { + dev_err(dev, "Data sent to IF7 from AD_OUT6 %d", + error); + return error; + } + } + return error; +} +int ste_audio_io_power_down_fmrx(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "FMRX should have mono or stereo channels"); + return -EINVAL; + } + if (channel_index & e_CHANNEL_1) { + /* data sent to DA7 input of DA filter form IF1 */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, 0, + SLOT24_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, "Clearing Data sent to DA_IN7 from Slot 24 %d", + error); + return error; + } + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0, + SEL_AD_OUT8_FROM_DAIN7); + if (0 != error) { + dev_err(dev, "Clearing Data sent to AD_OUT5 from DA_IN7 %d", + error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, 0, + SEL_IF6_FROM_AD_OUT5); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to IF slot 6 from AD_OUT5 %d", + error); + return error; + } +} + + if (channel_index & e_CHANNEL_2) { + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, 0, + SLOT25_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to DA_IN8 from Slot 25 %d", + error); + return error; + } + + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0, + SEL_AD_OUT6_FROM_DAIN8); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to AD_OUT6 from DA_IN8 %d", + error); + return error; + } + error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, 0, + SEL_IF7_FROM_AD_OUT6); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to IF7 from AD_OUT6 %d", + error); + return error; + } + } + return error; +} + +int ste_audio_io_power_up_fmtx(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal = 0; + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "FMTX should have mono or stereo channels"); + return -EINVAL; + } + + ste_audio_io_configure_if1(dev); + + if (channel_index & e_CHANNEL_1) { + /* data sent to DA7 input of DA filter form IF1 14 slot */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, + SLOT14_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, + "Data sent to DA_IN7 from Slot 14 %d", error); + return error; + } + /* DA_IN7 to AD_OUT5 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, + SEL_AD_OUT5_FROM_DAIN7, 0); + if (0 != error) { + dev_err(dev, "Data sent to AD_OUT5 from DA_IN7 %d", + error); + return error; + } + + initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT16_17_REG); + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG, + (initialVal & MASK_QUARTET1)|SEL_IF6_FROM_AD_OUT5); + if (0 != error) { + dev_err(dev, "Data sent to IF16 from AD_OUT5 %d", + error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* data sent to DA8 input of DA filter */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, + SLOT15_FOR_DA_PATH, 0); + if (0 != error) { + dev_err(dev, "Data sent to DA_IN8 from Slot 15 %d", + error); + return error; + } + + /* DA_IN8 to AD_OUT6 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, + SEL_AD_OUT6_FROM_DAIN8, 0); + if (0 != error) { + dev_err(dev, "Data sent to AD_OUT6 from DA_IN8 %d", + error); + return error; + } + + initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT16_17_REG); + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG, + (initialVal & MASK_QUARTET0)|SEL_IF17_FROM_AD_OUT6); + if (0 != error) { + dev_err(dev, "Data sent to IF17 from AD_OUT6 %d", + error); + return error; + } + } + return error; +} + +int ste_audio_io_power_down_fmtx(enum AUDIOIO_CH_INDEX channel_index, + struct device *dev) +{ + int error = 0; + unsigned char initialVal_AD = 0; + + if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) { + dev_err(dev, "FMTX should have mono or stereo channels"); + return -EINVAL; + } + + if (channel_index & e_CHANNEL_1) { + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, 0, + SLOT14_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to DA_IN7 from Slot 14 %d", + error); + return error; + } + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0, + SEL_AD_OUT5_FROM_DAIN7); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to AD_OUT5 from DA_IN7 %d", + error); + return error; + } + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG, + SEL_IF6_FROM_AD_OUT5); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to IF16 from AD_OUT8 %d", + error); + return error; + } + } + + if (channel_index & e_CHANNEL_2) { + /* data sent to DA8 input of DA filter */ + initialVal_AD = HW_REG_READ(SLOT_SELECTION_TO_DA8_REG); + + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, 0, + SLOT15_FOR_DA_PATH); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to DA_IN8 from Slot 15 %d", + error); + return error; + } + + /* DA_IN7 to AD_OUT8 path */ + error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0, + SEL_AD_OUT6_FROM_DAIN8); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to AD_OUT6 from DA_IN8 %d", + error); + return error; + } + error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG, + SEL_IF17_FROM_AD_OUT6); + if (0 != error) { + dev_err(dev, + "Clearing Data sent to IF17 from AD_OUT6 %d", + error); + return error; + } + } + return error; +} +int ste_audio_io_power_up_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev) +{ + int error = 0; + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); + struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev); + if (bluetooth_power_up_count++) + return error; + + if (pdata) { + if (pdata->audio) { + error = pdata->audio->ste_gpio_altf_init(); + if (error == 0) { + clk_ptr_msp0 = clk_get_sys("msp0", NULL); + if (!IS_ERR(clk_ptr_msp0)) { + error = clk_enable(clk_ptr_msp0); + return error; + } else + return -EFAULT; + } + } + } + return error; +} + +int ste_audio_io_power_down_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev) +{ + int error = 0; + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); + struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev); + + if (--bluetooth_power_up_count) + return error; + + if (pdata) { + if (pdata->audio) { + error = pdata->audio->ste_gpio_altf_exit(); + if (error == 0) { + clk_disable(clk_ptr_msp0); + clk_put(clk_ptr_msp0); + } + } + } + return error; +} + +int dump_acodec_registers(const char *str, struct device *dev) +{ + int reg_count = REVISION_REG & 0xff; + if (1 == acodec_reg_dump) { + u8 i = 0; + dev_info(dev, "\n func : %s\n", str); + for (i = 0; i <= reg_count; i++) + dev_info(dev, + "block = 0x0D, adr = %x = %x\n", + i, HW_REG_READ((AB8500_AUDIO << 8) | i)); + } + str = str; /* keep compiler happy */ + return 0; +} + +int debug_audioio(int x) +{ + + if (1 == x) + acodec_reg_dump = 1; + else + acodec_reg_dump = 0; + return 0; +} + + + + + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_func.h b/drivers/misc/audio_io_dev/ste_audio_io_func.h new file mode 100644 index 00000000000..282b25751d6 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_func.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef _AUDIOIO_FUNC_H_ +#define _AUDIOIO_FUNC_H_ + +#include <linux/string.h> +#include <linux/platform_device.h> +#include <mach/ste_audio_io_ioctl.h> +#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500.h> + +#define AB8500_REV_10 0x10 +#define AB8500_REV_11 0x11 +#define AB8500_REV_20 0x20 + +#define AB8500_CTRL3_REG 0x00000200 +#define AB8500_SYSULPCLK_CTRL1_REG 0x0000020B +#define AB8500_GPIO_DIR4_REG 0x00001013 +#define AB8500_GPIO_DIR5_REG 0x00001014 +#define AB8500_GPIO_OUT5_REG 0x00001024 + +extern struct platform_device *ste_audio_io_device; +extern struct regulator *regulator_avsource; + +int dump_acodec_registers(const char *, struct device *dev); +int debug_audioio(int x); + +#define AB8500_BLOCK_ADDR(address) ((address >> 8) & 0xff) +#define AB8500_OFFSET_ADDR(address) (address & 0xff) + +static inline unsigned char HW_REG_READ(unsigned short reg) +{ + unsigned char ret; + int err; + + err = abx500_get_register_interruptible(&ste_audio_io_device->dev, + AB8500_BLOCK_ADDR(reg), + AB8500_OFFSET_ADDR(reg), + &ret); + if (err < 0) + return err; + else + return ret; +} + +static inline int HW_REG_WRITE(unsigned short reg, unsigned char data) +{ + return abx500_set_register_interruptible(&ste_audio_io_device->dev, + AB8500_BLOCK_ADDR(reg), + AB8500_OFFSET_ADDR(reg), + data); +} + +unsigned int ab8500_acodec_modify_write(unsigned int reg, u8 mask_set, + u8 mask_clear); + +#define HW_ACODEC_MODIFY_WRITE(reg, mask_set, mask_clear)\ + ab8500_acodec_modify_write(reg, mask_set, mask_clear) + +unsigned int ab8500_modify_write(unsigned int reg, u8 mask_set, u8 mask_clear); + +int ste_audio_io_power_up_headset(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_headset(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_headset_query(struct device *dev); +int ste_audio_io_set_headset_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_headset_gain(int *, int *, u16, + struct device *dev); +int ste_audio_io_mute_headset(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_headset(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_headset_state(struct device *dev); +int ste_audio_io_enable_fade_headset(struct device *dev); +int ste_audio_io_disable_fade_headset(struct device *dev); +int ste_audio_io_switch_to_burst_mode_headset(int burst_fifo_switch_frame, + struct device *dev); +int ste_audio_io_switch_to_normal_mode_headset( + struct device *dev); + +int ste_audio_io_power_up_earpiece(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_earpiece(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_earpiece_query(struct device *dev); +int ste_audio_io_set_earpiece_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_earpiece_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_earpiece(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_earpiece(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_earpiece_state(struct device *dev); +int ste_audio_io_enable_fade_earpiece(struct device *dev); +int ste_audio_io_disable_fade_earpiece(struct device *dev); + +int ste_audio_io_power_up_ihf(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_ihf(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_ihf_query(struct device *dev); +int ste_audio_io_set_ihf_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_ihf_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_ihf(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_ihf(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_ihf_state(struct device *dev); +int ste_audio_io_enable_fade_ihf(struct device *dev); +int ste_audio_io_disable_fade_ihf(struct device *dev); + +int ste_audio_io_power_up_vibl(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_vibl(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_vibl_query(struct device *dev); +int ste_audio_io_set_vibl_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_vibl_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_vibl(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_vibl(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_vibl_state(struct device *dev); +int ste_audio_io_enable_fade_vibl(struct device *dev); +int ste_audio_io_disable_fade_vibl(struct device *dev); + +int ste_audio_io_power_up_vibr(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_vibr(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_vibr_query(struct device *dev); +int ste_audio_io_set_vibr_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_vibr_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_vibr(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_vibr(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_vibr_state(struct device *dev); +int ste_audio_io_enable_fade_vibr(struct device *dev); +int ste_audio_io_disable_fade_vibr(struct device *dev); + +int ste_audio_io_power_up_mic1a(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_mic1a(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_mic1a_query(struct device *dev); +int ste_audio_io_set_mic1a_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, struct device *dev); +int ste_audio_io_get_mic1a_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_mic1a(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_mic1a(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_mic1a_state(struct device *dev); +int ste_audio_io_enable_fade_mic1a(struct device *dev); +int ste_audio_io_disable_fade_mic1a(struct device *dev); + +/* + *** Mic1b *** + */ +int ste_audio_io_power_up_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_mic1b_query(struct device *dev); +int ste_audio_io_set_mic1b_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, struct device *dev); +int ste_audio_io_get_mic1b_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_mic1b(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_mic1b_state(struct device *dev); +int ste_audio_io_enable_fade_mic1b(struct device *dev); +int ste_audio_io_disable_fade_mic1b(struct device *dev); +int ste_audio_io_enable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS, + int loop_gain, struct device *dev, + void *cookie); +int ste_audio_io_disable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + struct device *dev, void *cookie); +/* + *** Mic2 *** + */ +int ste_audio_io_power_up_mic2(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_mic2(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_mic2_query(struct device *dev); +int ste_audio_io_set_mic2_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_mic2_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_mic2(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_mic2(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_mic2_state(struct device *dev); +int ste_audio_io_enable_fade_mic2(struct device *dev); +int ste_audio_io_disable_fade_mic2(struct device *dev); + +int ste_audio_io_power_up_lin(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_lin(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_lin_query(struct device *dev); +int ste_audio_io_set_lin_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_lin_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_lin(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_lin(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_lin_state(struct device *dev); +int ste_audio_io_enable_fade_lin(struct device *dev); +int ste_audio_io_disable_fade_lin(struct device *dev); + +int ste_audio_io_power_up_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_dmic12_query(struct device *dev); +int ste_audio_io_set_dmic12_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_dmic12_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_dmic12(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_dmic12_state(struct device *dev); +int ste_audio_io_enable_fade_dmic12(struct device *dev); +int ste_audio_io_disable_fade_dmic12(struct device *dev); +int ste_audio_io_enable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS, + int loop_gain, struct device *dev, + void *cookie); +int ste_audio_io_disable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index, + enum AUDIOIO_HAL_HW_LOOPS hw_loop, + struct device *dev, void *cookie); + +int ste_audio_io_power_up_dmic34(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_dmic34(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_dmic34_query(struct device *dev); +int ste_audio_io_set_dmic34_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_dmic34_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_dmic34(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_dmic34(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_dmic34_state(struct device *dev); +int ste_audio_io_enable_fade_dmic34(struct device *dev); +int ste_audio_io_disable_fade_dmic34(struct device *dev); + +int ste_audio_io_power_up_dmic56(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_dmic56(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_dmic56_query(struct device *dev); +int ste_audio_io_set_dmic56_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_dmic56_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_dmic56(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_dmic56(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_dmic56_state(struct device *dev); +int ste_audio_io_enable_fade_dmic56(struct device *dev); +int ste_audio_io_disable_fade_dmic56(struct device *dev); + +int ste_audio_io_power_up_fmrx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_fmrx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_fmrx_query(struct device *dev); +int ste_audio_io_set_fmrx_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_fmrx_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_fmrx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_fmrx(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_fmrx_state(struct device *dev); +int ste_audio_io_enable_fade_fmrx(struct device *dev); +int ste_audio_io_disable_fade_fmrx(struct device *dev); + +int ste_audio_io_power_up_fmtx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_fmtx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_fmtx_query(struct device *dev); +int ste_audio_io_set_fmtx_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index, + int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_fmtx_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_fmtx(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_fmtx(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_fmtx_state(struct device *dev); +int ste_audio_io_enable_fade_fmtx(struct device *dev); +int ste_audio_io_disable_fade_fmtx(struct device *dev); + +int ste_audio_io_power_up_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_down_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_power_state_bluetooth_query(struct device *dev); +int ste_audio_io_set_bluetooth_gain(enum AUDIOIO_CH_INDEX chnl_index, + u16 gain_index, int gain_value, u32 linear, + struct device *dev); +int ste_audio_io_get_bluetooth_gain(int*, int*, u16, + struct device *dev); +int ste_audio_io_mute_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, + struct device *dev); +int ste_audio_io_unmute_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, int *gain, + struct device *dev); +int ste_audio_io_mute_bluetooth_state(struct device *dev); +int ste_audio_io_enable_fade_bluetooth(struct device *dev); +int ste_audio_io_disable_fade_bluetooth(struct device *dev); + + +#endif + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c new file mode 100644 index 00000000000..c2409f849ae --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + + +#include <linux/types.h> +#include "ste_audio_io_hwctrl_common.h" + +/* Number of channels for each transducer */ +const uint transducer_no_of_channels[MAX_NO_TRANSDUCERS] = { + 1, /* Earpiece */ + 2, /* HS */ + 2, /* IHF */ + 1, /* VibL */ + 1, /* VibR */ + 1, /* Mic1A */ + 1, /* Mic1B */ + 1, /* Mic2 */ + 2, /* LinIn */ + 2, /* DMIC12 */ + 2, /* DMIC34 */ + 2, /* /DMIC56 */ + 4 /* MultiMic */ + }; + +/* Maximum number of gains in each transducer path + (all channels of a specific transducer have same max no of gains) */ +const uint transducer_no_of_gains[MAX_NO_TRANSDUCERS] = { + 2, /* Ear g3 and g4 */ + 3, /* HS g3 and g4 and analog */ + 1, /* IHF g3 */ + 1, /* VibL g3 */ + 1, /* VibR g3 */ + 2, /* Mic1A g1 and analog */ + 2, /* Mic1A g1 and analog */ + 2, /* Mic2 g1 and analog */ + 2, /* LinIn g1 and analog */ + 1, /* DMIC12 g1 */ + 1, /* DMIC34 g1 */ + 1, /* DMIC56 g1 */ + 1 /* MultiMic g1 */ + }; + +const uint transducer_no_Of_supported_loop_indexes[MAX_NO_TRANSDUCERS] = { + 0x09,/* Ear0x01|0x08*/ + 0x38770,/*{0x01|0x10|0x20|0x40|0x100*/ + /*|0x200|0x400|0x8000|0x10000|0x20000}, HS*/ + 0x86,/*IHF*/ + 0x0,/*VibL*/ + 0x0,/*VibR*/ + 0x0,/*Mic1A*/ + 0x01,/*Mic1B Sidetone is controlled*/ + 0x0,/*Mic2*/ + 0x0,/*LinIn*/ + 0x0,/*DMIC12*/ + 0x0,/*DMIC34*/ + 0x0,/*DMIC56*/ + 0x01,/*MultiMic Sidetone is controlled*/ + 0x0,/*FMRx*/ + 0x0/*FMTx*/ + }; + +const uint transducer_max_no_Of_supported_loops[MAX_NO_TRANSDUCERS] = { + 0,/*Ear Sidetone*/ + 2,/*HS SideTone LININ_HS LININR_HSR LININ_HSL*/ + 1,/*IHF TO BE DEFINED*/ + 0,/*VibL TO BE DEFINED*/ + 0,/*VibR TO BE DEFINED*/ + 1,/*Mic1A TO BE DEFINED*/ + 1,/*Mic1B SIDETONE TO BE DEFINED*/ + 1,/*Mic2 TO BE DEFINED*/ + 0, /* LinIn */ + 1,/*DMIC12-ANC*/ + 0,/*DMIC34-ANC*/ + 0, /* DMIC56 */ + 1,/*MultiMic-SIDETONE ANC*/ + 0,/*FMRx*/ + 0/*FMTx*/ + }; + +const uint max_no_of_loop_gains[MAX_NO_TRANSDUCERS] = { + 0,/*Earpiece*/ + 2,/*HS*/ + 0, + 0, + 0, + 0, + 2,/*Mic1B-Sidetone 2 gains*/ + 0, + 0, + 2,/*DMIC12*/ + 0, + 0, + 2,/*Multimic, Sidetone max no gains = 2*/ + 0, + 0 + }; + + +struct gain_descriptor_t gain_descriptor[MAX_NO_TRANSDUCERS]\ + [MAX_NO_CHANNELS][MAX_NO_GAINS] = { + /* gainIndex=0 1 2 + EDestinationEar */ + {{{-63, 0, 1}, {-1, 8, 1}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* EDestinationHS */ + {{{-63, 0, 1}, {-1, 8, 1}, {-32, 4, 2} } , /* channelIndex=0 */ + {{-63, 0, 1}, {-1, 8, 1}, {-32, 4, 2} } , /* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } , /* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } , /* channelIndex=3 */ + + /* EDestinationIHF */ + {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* EDestinationVibL */ + {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* EDestinationVibR */ + {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceMic1A */ + {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceMic1B */ + {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceMic2 */ + {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceLin */ + {{{-32, 31, 1}, {-10, 20, 2}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-32, 31, 1}, {-10, 20, 2}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceDMic12 */ + {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceDMic34 */ + {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceDMic56 */ + {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */ + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */ + + /* ESourceMultiMic */ + {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} },/* channelIndex=2 */ + {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } } /* channelIndex=3 */ +}; + + +const int hs_analog_gain_table[16] = {4, 2, 0, -2, -4, -6, -8, -10, + -12, -14, -16, -18, -20, -24, -28, -32}; + + + diff --git a/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h new file mode 100644 index 00000000000..cc2bfe21d81 --- /dev/null +++ b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef __AUDIOIO_HWCTRL_COMMON_H__ +#define __AUDIOIO_HWCTRL_COMMON_H__ + +#include <linux/types.h> +#include <mach/ste_audio_io_ioctl.h> +/* + * Defines + */ + +#define MAX_GAIN 100 +#define MIN_GAIN 0 +#define MAX_NO_CHANNELS 4 +#define MAX_NO_GAINS 3 +#define MAX_NO_LOOPS 1 +#define MAX_NO_LOOP_GAINS 1 + +struct gain_descriptor_t { + int min_gain; + int max_gain; + uint gain_step; +}; + + +/* Number of channels for each transducer */ +extern const uint transducer_no_of_channels[MAX_NO_TRANSDUCERS]; + +/* + * Maximum number of gains in each transducer path + * all channels of a specific transducer have same max no of gains + */ +extern const uint transducer_no_of_gains[MAX_NO_TRANSDUCERS]; + +/* Maximum number of supported loops for each transducer */ +extern const uint transducer_no_Of_supported_loop_indexes[MAX_NO_TRANSDUCERS]; +extern const uint transducer_max_no_Of_supported_loops[MAX_NO_TRANSDUCERS]; +extern const uint max_no_of_loop_gains[MAX_NO_TRANSDUCERS]; +extern const int hs_analog_gain_table[16] ; + +extern struct gain_descriptor_t gain_descriptor[MAX_NO_TRANSDUCERS]\ + [MAX_NO_CHANNELS][MAX_NO_GAINS]; + +#endif + +/* End of audio_io_hwctrl_common.h */ |