Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ca0132.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * HD audio codec driver for Creative CA0132 chip
 *
 * Copyright (c) 2011, Creative Technology Ltd.
 *
 * Based on ca0110.c
 * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
 */


#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"

#include "ca0132_regs.h"

/* Enable this to see controls for tuning purpose. */
#define ENABLE_TUNING_CONTROLS

#ifdef ENABLE_TUNING_CONTROLS
#include <sound/tlv.h>
#endif

#define FLOAT_ZERO 0x00000000
#define FLOAT_ONE 0x3f800000
#define FLOAT_TWO 0x40000000
#define FLOAT_THREE     0x40400000
#define FLOAT_FIVE 0x40a00000
#define FLOAT_SIX       0x40c00000
#define FLOAT_EIGHT     0x41000000
#define FLOAT_MINUS_5 0xc0a00000

#define UNSOL_TAG_DSP 0x16

#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)

#define DMA_TRANSFER_FRAME_SIZE_NWORDS  8
#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
#define DMA_OVERLAY_FRAME_SIZE_NWORDS  2

#define MASTERCONTROL    0x80
#define MASTERCONTROL_ALLOC_DMA_CHAN  10
#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60

#define WIDGET_CHIP_CTRL      0x15
#define WIDGET_DSP_CTRL       0x16

#define MEM_CONNID_MICIN1     3
#define MEM_CONNID_MICIN2     5
#define MEM_CONNID_MICOUT1    12
#define MEM_CONNID_MICOUT2    14
#define MEM_CONNID_WUH        10
#define MEM_CONNID_DSP        16
#define MEM_CONNID_DMIC       100

#define SCP_SET    0
#define SCP_GET    1

#define EFX_FILE   "ctefx.bin"
#define DESKTOP_EFX_FILE   "ctefx-desktop.bin"
#define R3DI_EFX_FILE  "ctefx-r3di.bin"

#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
MODULE_FIRMWARE(EFX_FILE);
MODULE_FIRMWARE(DESKTOP_EFX_FILE);
MODULE_FIRMWARE(R3DI_EFX_FILE);
#endif

static const char *const dirstr[2] = { "Playback""Capture" };

#define NUM_OF_OUTPUTS 2
static const char *const out_type_str[2] = { "Speakers""Headphone" };
enum {
 SPEAKER_OUT,
 HEADPHONE_OUT,
};

enum {
 DIGITAL_MIC,
 LINE_MIC_IN
};

/* Strings for Input Source Enum Control */
static const char *const in_src_str[3] = { "Microphone""Line In""Front Microphone" };
#define IN_SRC_NUM_OF_INPUTS 3
enum {
 REAR_MIC,
 REAR_LINE_IN,
 FRONT_MIC,
};

enum {
#define VNODE_START_NID    0x80
 VNID_SPK = VNODE_START_NID,   /* Speaker vnid */
 VNID_MIC,
 VNID_HP_SEL,
 VNID_AMIC1_SEL,
 VNID_HP_ASEL,
 VNID_AMIC1_ASEL,
 VNODE_END_NID,
#define VNODES_COUNT  (VNODE_END_NID - VNODE_START_NID)

#define EFFECT_START_NID    0x90
#define OUT_EFFECT_START_NID    EFFECT_START_NID
 SURROUND = OUT_EFFECT_START_NID,
 CRYSTALIZER,
 DIALOG_PLUS,
 SMART_VOLUME,
 X_BASS,
 EQUALIZER,
 OUT_EFFECT_END_NID,
#define OUT_EFFECTS_COUNT  (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)

#define IN_EFFECT_START_NID  OUT_EFFECT_END_NID
 ECHO_CANCELLATION = IN_EFFECT_START_NID,
 VOICE_FOCUS,
 MIC_SVM,
 NOISE_REDUCTION,
 IN_EFFECT_END_NID,
#define IN_EFFECTS_COUNT  (IN_EFFECT_END_NID - IN_EFFECT_START_NID)

 VOICEFX = IN_EFFECT_END_NID,
 PLAY_ENHANCEMENT,
 CRYSTAL_VOICE,
 EFFECT_END_NID,
 OUTPUT_SOURCE_ENUM,
 INPUT_SOURCE_ENUM,
 XBASS_XOVER,
 EQ_PRESET_ENUM,
 SMART_VOLUME_ENUM,
 MIC_BOOST_ENUM,
 AE5_HEADPHONE_GAIN_ENUM,
 AE5_SOUND_FILTER_ENUM,
 ZXR_HEADPHONE_GAIN,
 SPEAKER_CHANNEL_CFG_ENUM,
 SPEAKER_FULL_RANGE_FRONT,
 SPEAKER_FULL_RANGE_REAR,
 BASS_REDIRECTION,
 BASS_REDIRECTION_XOVER,
#define EFFECTS_COUNT  (EFFECT_END_NID - EFFECT_START_NID)
};

/* Effects values size*/
#define EFFECT_VALS_MAX_COUNT 12

/*
 * Default values for the effect slider controls, they are in order of their
 * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then
 * X-bass.
 */

static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50};
/* Amount of effect level sliders for ca0132_alt controls. */
#define EFFECT_LEVEL_SLIDERS 5

/* Latency introduced by DSP blocks in milliseconds. */
#define DSP_CAPTURE_INIT_LATENCY        0
#define DSP_CRYSTAL_VOICE_LATENCY       124
#define DSP_PLAYBACK_INIT_LATENCY       13
#define DSP_PLAY_ENHANCEMENT_LATENCY    30
#define DSP_SPEAKER_OUT_LATENCY         7

struct ct_effect {
 const char *name;
 hda_nid_t nid;
 int mid; /*effect module ID*/
 int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
 int direct; /* 0:output; 1:input*/
 int params; /* number of default non-on/off params */
 /*effect default values, 1st is on/off. */
 unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
};

#define EFX_DIR_OUT 0
#define EFX_DIR_IN  1

static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
 { .name = "Surround",
   .nid = SURROUND,
   .mid = 0x96,
   .reqs = {0, 1},
   .direct = EFX_DIR_OUT,
   .params = 1,
   .def_vals = {0x3F800000, 0x3F2B851F}
 },
 { .name = "Crystalizer",
   .nid = CRYSTALIZER,
   .mid = 0x96,
   .reqs = {7, 8},
   .direct = EFX_DIR_OUT,
   .params = 1,
   .def_vals = {0x3F800000, 0x3F266666}
 },
 { .name = "Dialog Plus",
   .nid = DIALOG_PLUS,
   .mid = 0x96,
   .reqs = {2, 3},
   .direct = EFX_DIR_OUT,
   .params = 1,
   .def_vals = {0x00000000, 0x3F000000}
 },
 { .name = "Smart Volume",
   .nid = SMART_VOLUME,
   .mid = 0x96,
   .reqs = {4, 5, 6},
   .direct = EFX_DIR_OUT,
   .params = 2,
   .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000}
 },
 { .name = "X-Bass",
   .nid = X_BASS,
   .mid = 0x96,
   .reqs = {24, 23, 25},
   .direct = EFX_DIR_OUT,
   .params = 2,
   .def_vals = {0x3F800000, 0x42A00000, 0x3F000000}
 },
 { .name = "Equalizer",
   .nid = EQUALIZER,
   .mid = 0x96,
   .reqs = {9, 10, 11, 12, 13, 14,
   15, 16, 17, 18, 19, 20},
   .direct = EFX_DIR_OUT,
   .params = 11,
   .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
         0x00000000, 0x00000000, 0x00000000, 0x00000000,
         0x00000000, 0x00000000, 0x00000000, 0x00000000}
 },
 { .name = "Echo Cancellation",
   .nid = ECHO_CANCELLATION,
   .mid = 0x95,
   .reqs = {0, 1, 2, 3},
   .direct = EFX_DIR_IN,
   .params = 3,
   .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
 },
 { .name = "Voice Focus",
   .nid = VOICE_FOCUS,
   .mid = 0x95,
   .reqs = {6, 7, 8, 9},
   .direct = EFX_DIR_IN,
   .params = 3,
   .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
 },
 { .name = "Mic SVM",
   .nid = MIC_SVM,
   .mid = 0x95,
   .reqs = {44, 45},
   .direct = EFX_DIR_IN,
   .params = 1,
   .def_vals = {0x00000000, 0x3F3D70A4}
 },
 { .name = "Noise Reduction",
   .nid = NOISE_REDUCTION,
   .mid = 0x95,
   .reqs = {4, 5},
   .direct = EFX_DIR_IN,
   .params = 1,
   .def_vals = {0x3F800000, 0x3F000000}
 },
 { .name = "VoiceFX",
   .nid = VOICEFX,
   .mid = 0x95,
   .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18},
   .direct = EFX_DIR_IN,
   .params = 8,
   .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000,
         0x3F800000, 0x3F800000, 0x3F800000, 0x00000000,
         0x00000000}
 }
};

/* Tuning controls */
#ifdef ENABLE_TUNING_CONTROLS

enum {
#define TUNING_CTL_START_NID  0xC0
 WEDGE_ANGLE = TUNING_CTL_START_NID,
 SVM_LEVEL,
 EQUALIZER_BAND_0,
 EQUALIZER_BAND_1,
 EQUALIZER_BAND_2,
 EQUALIZER_BAND_3,
 EQUALIZER_BAND_4,
 EQUALIZER_BAND_5,
 EQUALIZER_BAND_6,
 EQUALIZER_BAND_7,
 EQUALIZER_BAND_8,
 EQUALIZER_BAND_9,
 TUNING_CTL_END_NID
#define TUNING_CTLS_COUNT  (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
};

struct ct_tuning_ctl {
 const char *name;
 hda_nid_t parent_nid;
 hda_nid_t nid;
 int mid; /*effect module ID*/
 int req; /*effect module request*/
 int direct; /* 0:output; 1:input*/
 unsigned int def_val;/*effect default values*/
};

static const struct ct_tuning_ctl ca0132_tuning_ctls[] = {
 { .name = "Wedge Angle",
   .parent_nid = VOICE_FOCUS,
   .nid = WEDGE_ANGLE,
   .mid = 0x95,
   .req = 8,
   .direct = EFX_DIR_IN,
   .def_val = 0x41F00000
 },
 { .name = "SVM Level",
   .parent_nid = MIC_SVM,
   .nid = SVM_LEVEL,
   .mid = 0x95,
   .req = 45,
   .direct = EFX_DIR_IN,
   .def_val = 0x3F3D70A4
 },
 { .name = "EQ Band0",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_0,
   .mid = 0x96,
   .req = 11,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band1",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_1,
   .mid = 0x96,
   .req = 12,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band2",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_2,
   .mid = 0x96,
   .req = 13,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band3",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_3,
   .mid = 0x96,
   .req = 14,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band4",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_4,
   .mid = 0x96,
   .req = 15,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band5",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_5,
   .mid = 0x96,
   .req = 16,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band6",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_6,
   .mid = 0x96,
   .req = 17,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band7",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_7,
   .mid = 0x96,
   .req = 18,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band8",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_8,
   .mid = 0x96,
   .req = 19,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 },
 { .name = "EQ Band9",
   .parent_nid = EQUALIZER,
   .nid = EQUALIZER_BAND_9,
   .mid = 0x96,
   .req = 20,
   .direct = EFX_DIR_OUT,
   .def_val = 0x00000000
 }
};
#endif

/* Voice FX Presets */
#define VOICEFX_MAX_PARAM_COUNT 9

struct ct_voicefx {
 const char *name;
 hda_nid_t nid;
 int mid;
 int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
};

struct ct_voicefx_preset {
 const char *name; /*preset name*/
 unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
};

static const struct ct_voicefx ca0132_voicefx = {
 .name = "VoiceFX Capture Switch",
 .nid = VOICEFX,
 .mid = 0x95,
 .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
};

static const struct ct_voicefx_preset ca0132_voicefx_presets[] = {
 { .name = "Neutral",
   .vals = { 0x00000000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x3F800000, 0x3F800000,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Female2Male",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x3F19999A, 0x3F866666,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Male2Female",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x450AC000, 0x4017AE14, 0x3F6B851F,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "ScrappyKid",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x40400000, 0x3F28F5C3,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Elderly",
   .vals = { 0x3F800000, 0x44324000, 0x44BB8000,
      0x44E10000, 0x3FB33333, 0x3FB9999A,
      0x3F800000, 0x3E3A2E43, 0x00000000 }
 },
 { .name = "Orc",
   .vals = { 0x3F800000, 0x43EA0000, 0x44A52000,
      0x45098000, 0x3F266666, 0x3FC00000,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Elf",
   .vals = { 0x3F800000, 0x43C70000, 0x44AE6000,
      0x45193000, 0x3F8E147B, 0x3F75C28F,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Dwarf",
   .vals = { 0x3F800000, 0x43930000, 0x44BEE000,
      0x45007000, 0x3F451EB8, 0x3F7851EC,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "AlienBrute",
   .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
      0x451F6000, 0x3F266666, 0x3FA7D945,
      0x3F800000, 0x3CF5C28F, 0x00000000 }
 },
 { .name = "Robot",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x3FB2718B, 0x3F800000,
      0xBC07010E, 0x00000000, 0x00000000 }
 },
 { .name = "Marine",
   .vals = { 0x3F800000, 0x43C20000, 0x44906000,
      0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
      0x3F0A3D71, 0x00000000, 0x00000000 }
 },
 { .name = "Emo",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x3F800000, 0x3F800000,
      0x3E4CCCCD, 0x00000000, 0x00000000 }
 },
 { .name = "DeepVoice",
   .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
      0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
      0x3F800000, 0x00000000, 0x00000000 }
 },
 { .name = "Munchkin",
   .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
      0x44FA0000, 0x3F800000, 0x3F1A043C,
      0x3F800000, 0x00000000, 0x00000000 }
 }
};

/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */

#define EQ_PRESET_MAX_PARAM_COUNT 11

struct ct_eq {
 const char *name;
 hda_nid_t nid;
 int mid;
 int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
};

struct ct_eq_preset {
 const char *name; /*preset name*/
 unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT];
};

static const struct ct_eq ca0132_alt_eq_enum = {
 .name = "FX: Equalizer Preset Switch",
 .nid = EQ_PRESET_ENUM,
 .mid = 0x96,
 .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
};


static const struct ct_eq_preset ca0132_alt_eq_presets[] = {
 { .name = "Flat",
  .vals = { 0x00000000, 0x00000000, 0x00000000,
     0x00000000, 0x00000000, 0x00000000,
     0x00000000, 0x00000000, 0x00000000,
     0x00000000, 0x00000000      }
 },
 { .name = "Acoustic",
  .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD,
     0x40000000, 0x00000000, 0x00000000,
     0x00000000, 0x00000000, 0x40000000,
     0x40000000, 0x40000000      }
 },
 { .name = "Classical",
  .vals = { 0x00000000, 0x00000000, 0x40C00000,
     0x40C00000, 0x40466666, 0x00000000,
     0x00000000, 0x00000000, 0x00000000,
     0x40466666, 0x40466666      }
 },
 { .name = "Country",
  .vals = { 0x00000000, 0xBF99999A, 0x00000000,
     0x3FA66666, 0x3FA66666, 0x3F8CCCCD,
     0x00000000, 0x00000000, 0x40000000,
     0x40466666, 0x40800000      }
 },
 { .name = "Dance",
  .vals = { 0x00000000, 0xBF99999A, 0x40000000,
     0x40466666, 0x40866666, 0xBF99999A,
     0xBF99999A, 0x00000000, 0x00000000,
     0x40800000, 0x40800000      }
 },
 { .name = "Jazz",
  .vals = { 0x00000000, 0x00000000, 0x00000000,
     0x3F8CCCCD, 0x40800000, 0x40800000,
     0x40800000, 0x00000000, 0x3F8CCCCD,
     0x40466666, 0x40466666      }
 },
 { .name = "New Age",
  .vals = { 0x00000000, 0x00000000, 0x40000000,
     0x40000000, 0x00000000, 0x00000000,
     0x00000000, 0x3F8CCCCD, 0x40000000,
     0x40000000, 0x40000000      }
 },
 { .name = "Pop",
  .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000,
     0x40000000, 0x40000000, 0x00000000,
     0xBF99999A, 0xBF99999A, 0x00000000,
     0x40466666, 0x40C00000      }
 },
 { .name = "Rock",
  .vals = { 0x00000000, 0xBF99999A, 0xBF99999A,
     0x3F8CCCCD, 0x40000000, 0xBF99999A,
     0xBF99999A, 0x00000000, 0x00000000,
     0x40800000, 0x40800000      }
 },
 { .name = "Vocal",
  .vals = { 0x00000000, 0xC0000000, 0xBF99999A,
     0xBF99999A, 0x00000000, 0x40466666,
     0x40800000, 0x40466666, 0x00000000,
     0x00000000, 0x3F8CCCCD      }
 }
};

/*
 * DSP reqs for handling full-range speakers/bass redirection. If a speaker is
 * set as not being full range, and bass redirection is enabled, all
 * frequencies below the crossover frequency are redirected to the LFE
 * channel. If the surround configuration has no LFE channel, this can't be
 * enabled. X-Bass must be disabled when using these.
 */

enum speaker_range_reqs {
 SPEAKER_BASS_REDIRECT            = 0x15,
 SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16,
 /* Between 0x16-0x1a are the X-Bass reqs. */
 SPEAKER_FULL_RANGE_FRONT_L_R     = 0x1a,
 SPEAKER_FULL_RANGE_CENTER_LFE    = 0x1b,
 SPEAKER_FULL_RANGE_REAR_L_R      = 0x1c,
 SPEAKER_FULL_RANGE_SURROUND_L_R  = 0x1d,
 SPEAKER_BASS_REDIRECT_SUB_GAIN   = 0x1e,
};

/*
 * Definitions for the DSP req's to handle speaker tuning. These all belong to
 * module ID 0x96, the output effects module.
 */

enum speaker_tuning_reqs {
 /*
 * Currently, this value is always set to 0.0f. However, on Windows,
 * when selecting certain headphone profiles on the new Sound Blaster
 * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is
 * sent. This gets the speaker EQ address area, which is then used to
 * send over (presumably) an equalizer profile for the specific
 * headphone setup. It is sent using the same method the DSP
 * firmware is uploaded with, which I believe is why the 'ctspeq.bin'
 * file exists in linux firmware tree but goes unused. It would also
 * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused.
 * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is
 * set to 1.0f.
 */

 SPEAKER_TUNING_USE_SPEAKER_EQ           = 0x1f,
 SPEAKER_TUNING_ENABLE_CENTER_EQ         = 0x20,
 SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL     = 0x21,
 SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL    = 0x22,
 SPEAKER_TUNING_CENTER_VOL_LEVEL         = 0x23,
 SPEAKER_TUNING_LFE_VOL_LEVEL            = 0x24,
 SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL      = 0x25,
 SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL     = 0x26,
 SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL  = 0x27,
 SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28,
 /*
 * Inversion is used when setting headphone virtualization to line
 * out. Not sure why this is, but it's the only place it's ever used.
 */

 SPEAKER_TUNING_FRONT_LEFT_INVERT        = 0x29,
 SPEAKER_TUNING_FRONT_RIGHT_INVERT       = 0x2a,
 SPEAKER_TUNING_CENTER_INVERT            = 0x2b,
 SPEAKER_TUNING_LFE_INVERT               = 0x2c,
 SPEAKER_TUNING_REAR_LEFT_INVERT         = 0x2d,
 SPEAKER_TUNING_REAR_RIGHT_INVERT        = 0x2e,
 SPEAKER_TUNING_SURROUND_LEFT_INVERT     = 0x2f,
 SPEAKER_TUNING_SURROUND_RIGHT_INVERT    = 0x30,
 /* Delay is used when setting surround speaker distance in Windows. */
 SPEAKER_TUNING_FRONT_LEFT_DELAY         = 0x31,
 SPEAKER_TUNING_FRONT_RIGHT_DELAY        = 0x32,
 SPEAKER_TUNING_CENTER_DELAY             = 0x33,
 SPEAKER_TUNING_LFE_DELAY                = 0x34,
 SPEAKER_TUNING_REAR_LEFT_DELAY          = 0x35,
 SPEAKER_TUNING_REAR_RIGHT_DELAY         = 0x36,
 SPEAKER_TUNING_SURROUND_LEFT_DELAY      = 0x37,
 SPEAKER_TUNING_SURROUND_RIGHT_DELAY     = 0x38,
 /* Of these two, only mute seems to ever be used. */
 SPEAKER_TUNING_MAIN_VOLUME              = 0x39,
 SPEAKER_TUNING_MUTE                     = 0x3a,
};

/* Surround output channel count configuration structures. */
#define SPEAKER_CHANNEL_CFG_COUNT 5
enum {
 SPEAKER_CHANNELS_2_0,
 SPEAKER_CHANNELS_2_1,
 SPEAKER_CHANNELS_4_0,
 SPEAKER_CHANNELS_4_1,
 SPEAKER_CHANNELS_5_1,
};

struct ca0132_alt_speaker_channel_cfg {
 const char *name;
 unsigned int val;
};

static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = {
 { .name = "2.0",
   .val = FLOAT_ONE
 },
 { .name = "2.1",
   .val = FLOAT_TWO
 },
 { .name = "4.0",
   .val = FLOAT_FIVE
 },
 { .name = "4.1",
   .val = FLOAT_SIX
 },
 { .name = "5.1",
   .val = FLOAT_EIGHT
 }
};

/*
 * DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
 * and I don't know what the third req is, but it's always zero. I assume it's
 * some sort of update or set command to tell the DSP there's new volume info.
 */

#define DSP_VOL_OUT 0
#define DSP_VOL_IN  1

struct ct_dsp_volume_ctl {
 hda_nid_t vnid;
 int mid; /* module ID*/
 unsigned int reqs[3]; /* scp req ID */
};

static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
 { .vnid = VNID_SPK,
   .mid = 0x32,
   .reqs = {3, 4, 2}
 },
 { .vnid = VNID_MIC,
   .mid = 0x37,
   .reqs = {2, 3, 1}
 }
};

/* Values for ca0113_mmio_command_set for selecting output. */
#define AE_CA0113_OUT_SET_COMMANDS 6
struct ae_ca0113_output_set {
 unsigned int group[AE_CA0113_OUT_SET_COMMANDS];
 unsigned int target[AE_CA0113_OUT_SET_COMMANDS];
 unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS];
};

static const struct ae_ca0113_output_set ae5_ca0113_output_presets = {
 .group =  { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
 .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
      /* Speakers. */
 .vals =   { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
      /* Headphones. */
      { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } },
};

static const struct ae_ca0113_output_set ae7_ca0113_output_presets = {
 .group  = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
 .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
      /* Speakers. */
 .vals   = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
      /* Headphones. */
      { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } },
};

/* ae5 ca0113 command sequences to set headphone gain levels. */
#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4
struct ae5_headphone_gain_set {
 const char *name;
 unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS];
};

static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = {
 { .name = "Low (16-31",
   .vals = { 0xff, 0x2c, 0xf5, 0x32 }
 },
 { .name = "Medium (32-149",
   .vals = { 0x38, 0xa8, 0x3e, 0x4c }
 },
 { .name = "High (150-600",
   .vals = { 0xff, 0xff, 0xff, 0x7f }
 }
};

struct ae5_filter_set {
 const char *name;
 unsigned int val;
};

static const struct ae5_filter_set ae5_filter_presets[] = {
 { .name = "Slow Roll Off",
   .val = 0xa0
 },
 { .name = "Minimum Phase",
   .val = 0xc0
 },
 { .name = "Fast Roll Off",
   .val = 0x80
 }
};

/*
 * Data structures for storing audio router remapping data. These are used to
 * remap a currently active streams ports.
 */

struct chipio_stream_remap_data {
 unsigned int stream_id;
 unsigned int count;

 unsigned int offset[16];
 unsigned int value[16];
};

static const struct chipio_stream_remap_data stream_remap_data[] = {
 { .stream_id = 0x14,
   .count     = 0x04,
   .offset    = { 0x00, 0x04, 0x08, 0x0c },
   .value     = { 0x0001f8c0, 0x0001f9c1, 0x0001fac6, 0x0001fbc7 },
 },
 { .stream_id = 0x0c,
   .count     = 0x0c,
   .offset    = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
    0x20, 0x24, 0x28, 0x2c },
   .value     = { 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3,
    0x0001e2c4, 0x0001e3c5, 0x0001e8c6, 0x0001e9c7,
    0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb },
 },
 { .stream_id = 0x0c,
   .count     = 0x08,
   .offset    = { 0x08, 0x0c, 0x10, 0x14, 0x20, 0x24, 0x28, 0x2c },
   .value     = { 0x000140c2, 0x000141c3, 0x000150c4, 0x000151c5,
    0x000142c8, 0x000143c9, 0x000152ca, 0x000153cb },
 }
};

enum hda_cmd_vendor_io {
 /* for DspIO node */
 VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
 VENDOR_DSPIO_SCP_WRITE_DATA_HIGH     = 0x100,

 VENDOR_DSPIO_STATUS                  = 0xF01,
 VENDOR_DSPIO_SCP_POST_READ_DATA      = 0x702,
 VENDOR_DSPIO_SCP_READ_DATA           = 0xF02,
 VENDOR_DSPIO_DSP_INIT                = 0x703,
 VENDOR_DSPIO_SCP_POST_COUNT_QUERY    = 0x704,
 VENDOR_DSPIO_SCP_READ_COUNT          = 0xF04,

 /* for ChipIO node */
 VENDOR_CHIPIO_ADDRESS_LOW            = 0x000,
 VENDOR_CHIPIO_ADDRESS_HIGH           = 0x100,
 VENDOR_CHIPIO_STREAM_FORMAT          = 0x200,
 VENDOR_CHIPIO_DATA_LOW               = 0x300,
 VENDOR_CHIPIO_DATA_HIGH              = 0x400,

 VENDOR_CHIPIO_8051_WRITE_DIRECT      = 0x500,
 VENDOR_CHIPIO_8051_READ_DIRECT       = 0xD00,

 VENDOR_CHIPIO_GET_PARAMETER          = 0xF00,
 VENDOR_CHIPIO_STATUS                 = 0xF01,
 VENDOR_CHIPIO_HIC_POST_READ          = 0x702,
 VENDOR_CHIPIO_HIC_READ_DATA          = 0xF03,

 VENDOR_CHIPIO_8051_DATA_WRITE        = 0x707,
 VENDOR_CHIPIO_8051_DATA_READ         = 0xF07,
 VENDOR_CHIPIO_8051_PMEM_READ         = 0xF08,
 VENDOR_CHIPIO_8051_IRAM_WRITE        = 0x709,
 VENDOR_CHIPIO_8051_IRAM_READ         = 0xF09,

 VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE   = 0x70A,
 VENDOR_CHIPIO_CT_EXTENSIONS_GET      = 0xF0A,

 VENDOR_CHIPIO_PLL_PMU_WRITE          = 0x70C,
 VENDOR_CHIPIO_PLL_PMU_READ           = 0xF0C,
 VENDOR_CHIPIO_8051_ADDRESS_LOW       = 0x70D,
 VENDOR_CHIPIO_8051_ADDRESS_HIGH      = 0x70E,
 VENDOR_CHIPIO_FLAG_SET               = 0x70F,
 VENDOR_CHIPIO_FLAGS_GET              = 0xF0F,
 VENDOR_CHIPIO_PARAM_SET              = 0x710,
 VENDOR_CHIPIO_PARAM_GET              = 0xF10,

 VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET  = 0x711,
 VENDOR_CHIPIO_PORT_ALLOC_SET         = 0x712,
 VENDOR_CHIPIO_PORT_ALLOC_GET         = 0xF12,
 VENDOR_CHIPIO_PORT_FREE_SET          = 0x713,

 VENDOR_CHIPIO_PARAM_EX_ID_GET        = 0xF17,
 VENDOR_CHIPIO_PARAM_EX_ID_SET        = 0x717,
 VENDOR_CHIPIO_PARAM_EX_VALUE_GET     = 0xF18,
 VENDOR_CHIPIO_PARAM_EX_VALUE_SET     = 0x718,

 VENDOR_CHIPIO_DMIC_CTL_SET           = 0x788,
 VENDOR_CHIPIO_DMIC_CTL_GET           = 0xF88,
 VENDOR_CHIPIO_DMIC_PIN_SET           = 0x789,
 VENDOR_CHIPIO_DMIC_PIN_GET           = 0xF89,
 VENDOR_CHIPIO_DMIC_MCLK_SET          = 0x78A,
 VENDOR_CHIPIO_DMIC_MCLK_GET          = 0xF8A,

 VENDOR_CHIPIO_EAPD_SEL_SET           = 0x78D
};

/*
 *  Control flag IDs
 */

enum control_flag_id {
 /* Connection manager stream setup is bypassed/enabled */
 CONTROL_FLAG_C_MGR                  = 0,
 /* DSP DMA is bypassed/enabled */
 CONTROL_FLAG_DMA                    = 1,
 /* 8051 'idle' mode is disabled/enabled */
 CONTROL_FLAG_IDLE_ENABLE            = 2,
 /* Tracker for the SPDIF-in path is bypassed/enabled */
 CONTROL_FLAG_TRACKER                = 3,
 /* DigitalOut to Spdif2Out connection is disabled/enabled */
 CONTROL_FLAG_SPDIF2OUT              = 4,
 /* Digital Microphone is disabled/enabled */
 CONTROL_FLAG_DMIC                   = 5,
 /* ADC_B rate is 48 kHz/96 kHz */
 CONTROL_FLAG_ADC_B_96KHZ            = 6,
 /* ADC_C rate is 48 kHz/96 kHz */
 CONTROL_FLAG_ADC_C_96KHZ            = 7,
 /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
 CONTROL_FLAG_DAC_96KHZ              = 8,
 /* DSP rate is 48 kHz/96 kHz */
 CONTROL_FLAG_DSP_96KHZ              = 9,
 /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
 CONTROL_FLAG_SRC_CLOCK_196MHZ       = 10,
 /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
 CONTROL_FLAG_SRC_RATE_96KHZ         = 11,
 /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
 CONTROL_FLAG_DECODE_LOOP            = 12,
 /* De-emphasis filter on DAC-1 disabled/enabled */
 CONTROL_FLAG_DAC1_DEEMPHASIS        = 13,
 /* De-emphasis filter on DAC-2 disabled/enabled */
 CONTROL_FLAG_DAC2_DEEMPHASIS        = 14,
 /* De-emphasis filter on DAC-3 disabled/enabled */
 CONTROL_FLAG_DAC3_DEEMPHASIS        = 15,
 /* High-pass filter on ADC_B disabled/enabled */
 CONTROL_FLAG_ADC_B_HIGH_PASS        = 16,
 /* High-pass filter on ADC_C disabled/enabled */
 CONTROL_FLAG_ADC_C_HIGH_PASS        = 17,
 /* Common mode on Port_A disabled/enabled */
 CONTROL_FLAG_PORT_A_COMMON_MODE     = 18,
 /* Common mode on Port_D disabled/enabled */
 CONTROL_FLAG_PORT_D_COMMON_MODE     = 19,
 /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
 CONTROL_FLAG_PORT_A_10KOHM_LOAD     = 20,
 /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
 CONTROL_FLAG_PORT_D_10KOHM_LOAD     = 21,
 /* ASI rate is 48kHz/96kHz */
 CONTROL_FLAG_ASI_96KHZ              = 22,
 /* DAC power settings able to control attached ports no/yes */
 CONTROL_FLAG_DACS_CONTROL_PORTS     = 23,
 /* Clock Stop OK reporting is disabled/enabled */
 CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
 /* Number of control flags */
 CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
};

/*
 * Control parameter IDs
 */

enum control_param_id {
 /* 0: None, 1: Mic1In*/
 CONTROL_PARAM_VIP_SOURCE               = 1,
 /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
 CONTROL_PARAM_SPDIF1_SOURCE            = 2,
 /* Port A output stage gain setting to use when 16 Ohm output
 * impedance is selected*/

 CONTROL_PARAM_PORTA_160OHM_GAIN        = 8,
 /* Port D output stage gain setting to use when 16 Ohm output
 * impedance is selected*/

 CONTROL_PARAM_PORTD_160OHM_GAIN        = 10,

 /*
 * This control param name was found in the 8051 memory, and makes
 * sense given the fact the AE-5 uses it and has the ASI flag set.
 */

 CONTROL_PARAM_ASI                      = 23,

 /* Stream Control */

 /* Select stream with the given ID */
 CONTROL_PARAM_STREAM_ID                = 24,
 /* Source connection point for the selected stream */
 CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
 /* Destination connection point for the selected stream */
 CONTROL_PARAM_STREAM_DEST_CONN_POINT   = 26,
 /* Number of audio channels in the selected stream */
 CONTROL_PARAM_STREAMS_CHANNELS         = 27,
 /*Enable control for the selected stream */
 CONTROL_PARAM_STREAM_CONTROL           = 28,

 /* Connection Point Control */

 /* Select connection point with the given ID */
 CONTROL_PARAM_CONN_POINT_ID            = 29,
 /* Connection point sample rate */
 CONTROL_PARAM_CONN_POINT_SAMPLE_RATE   = 30,

 /* Node Control */

 /* Select HDA node with the given ID */
 CONTROL_PARAM_NODE_ID                  = 31
};

/*
 *  Dsp Io Status codes
 */

enum hda_vendor_status_dspio {
 /* Success */
 VENDOR_STATUS_DSPIO_OK                       = 0x00,
 /* Busy, unable to accept new command, the host must retry */
 VENDOR_STATUS_DSPIO_BUSY                     = 0x01,
 /* SCP command queue is full */
 VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL   = 0x02,
 /* SCP response queue is empty */
 VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
};

/*
 *  Chip Io Status codes
 */

enum hda_vendor_status_chipio {
 /* Success */
 VENDOR_STATUS_CHIPIO_OK   = 0x00,
 /* Busy, unable to accept new command, the host must retry */
 VENDOR_STATUS_CHIPIO_BUSY = 0x01
};

/*
 *  CA0132 sample rate
 */

enum ca0132_sample_rate {
 SR_6_000        = 0x00,
 SR_8_000        = 0x01,
 SR_9_600        = 0x02,
 SR_11_025       = 0x03,
 SR_16_000       = 0x04,
 SR_22_050       = 0x05,
 SR_24_000       = 0x06,
 SR_32_000       = 0x07,
 SR_44_100       = 0x08,
 SR_48_000       = 0x09,
 SR_88_200       = 0x0A,
 SR_96_000       = 0x0B,
 SR_144_000      = 0x0C,
 SR_176_400      = 0x0D,
 SR_192_000      = 0x0E,
 SR_384_000      = 0x0F,

 SR_COUNT        = 0x10,

 SR_RATE_UNKNOWN = 0x1F
};

enum dsp_download_state {
 DSP_DOWNLOAD_FAILED = -1,
 DSP_DOWNLOAD_INIT   = 0,
 DSP_DOWNLOADING     = 1,
 DSP_DOWNLOADED      = 2
};

/* retrieve parameters from hda format */
#define get_hdafmt_chs(fmt) (fmt & 0xf)
#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)

/*
 * CA0132 specific
 */


struct ca0132_spec {
 const struct snd_kcontrol_new *mixers[5];
 unsigned int num_mixers;
 const struct hda_verb *base_init_verbs;
 const struct hda_verb *base_exit_verbs;
 const struct hda_verb *chip_init_verbs;
 const struct hda_verb *desktop_init_verbs;
 struct hda_verb *spec_init_verbs;
 struct auto_pin_cfg autocfg;

 /* Nodes configurations */
 struct hda_multi_out multiout;
 hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
 hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
 unsigned int num_outputs;
 hda_nid_t input_pins[AUTO_PIN_LAST];
 hda_nid_t adcs[AUTO_PIN_LAST];
 hda_nid_t dig_out;
 hda_nid_t dig_in;
 unsigned int num_inputs;
 hda_nid_t shared_mic_nid;
 hda_nid_t shared_out_nid;
 hda_nid_t unsol_tag_hp;
 hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */
 hda_nid_t unsol_tag_amic1;

 /* chip access */
 struct mutex chipio_mutex; /* chip access mutex */
 u32 curr_chip_addx;

 /* DSP download related */
 enum dsp_download_state dsp_state;
 unsigned int dsp_stream_id;
 unsigned int wait_scp;
 unsigned int wait_scp_header;
 unsigned int wait_num_data;
 unsigned int scp_resp_header;
 unsigned int scp_resp_data[4];
 unsigned int scp_resp_count;
 bool startup_check_entered;
 bool dsp_reload;

 /* mixer and effects related */
 unsigned char dmic_ctl;
 int cur_out_type;
 int cur_mic_type;
 long vnode_lvol[VNODES_COUNT];
 long vnode_rvol[VNODES_COUNT];
 long vnode_lswitch[VNODES_COUNT];
 long vnode_rswitch[VNODES_COUNT];
 long effects_switch[EFFECTS_COUNT];
 long voicefx_val;
 long cur_mic_boost;
 /* ca0132_alt control related values */
 unsigned char in_enum_val;
 unsigned char out_enum_val;
 unsigned char channel_cfg_val;
 unsigned char speaker_range_val[2];
 unsigned char mic_boost_enum_val;
 unsigned char smart_volume_setting;
 unsigned char bass_redirection_val;
 long bass_redirect_xover_freq;
 long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
 long xbass_xover_freq;
 long eq_preset_val;
 unsigned int tlv[4];
 struct hda_vmaster_mute_hook vmaster_mute;
 /* AE-5 Control values */
 unsigned char ae5_headphone_gain_val;
 unsigned char ae5_filter_val;
 /* ZxR Control Values */
 unsigned char zxr_gain_set;

 struct hda_codec *codec;
 struct delayed_work unsol_hp_work;

#ifdef ENABLE_TUNING_CONTROLS
 long cur_ctl_vals[TUNING_CTLS_COUNT];
#endif
 /*
 * The Recon3D, Sound Blaster Z, Sound Blaster ZxR, and Sound Blaster
 * AE-5 all use PCI region 2 to toggle GPIO and other currently unknown
 * things.
 */

 bool use_pci_mmio;
 void __iomem *mem_base;

 /*
 * Whether or not to use the alt functions like alt_select_out,
 * alt_select_in, etc. Only used on desktop codecs for now, because of
 * surround sound support.
 */

 bool use_alt_functions;

 /*
 * Whether or not to use alt controls: volume effect sliders, EQ
 * presets, smart volume presets, and new control names with FX prefix.
 * Renames PlayEnhancement and CrystalVoice too.
 */

 bool use_alt_controls;
};

/*
 * CA0132 quirks table
 */

enum {
 QUIRK_ALIENWARE,
 QUIRK_ALIENWARE_M17XR4,
 QUIRK_SBZ,
 QUIRK_ZXR,
 QUIRK_ZXR_DBPRO,
 QUIRK_R3DI,
 QUIRK_R3D,
 QUIRK_AE5,
 QUIRK_AE7,
 QUIRK_NONE = HDA_FIXUP_ID_NOT_SET,
};

#ifdef CONFIG_PCI
#define ca0132_quirk(spec)  ((spec)->codec->fixup_id)
#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio)
#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions)
#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls)
#else
#define ca0132_quirk(spec)  ({ (void)(spec); QUIRK_NONE; })
#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; })
#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; })
#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; })
#endif

static const struct hda_pintbl alienware_pincfgs[] = {
 { 0x0b, 0x90170110 }, /* Builtin Speaker */
 { 0x0c, 0x411111f0 }, /* N/A */
 { 0x0d, 0x411111f0 }, /* N/A */
 { 0x0e, 0x411111f0 }, /* N/A */
 { 0x0f, 0x0321101f }, /* HP */
 { 0x10, 0x411111f0 }, /* Headset?  disabled for now */
 { 0x11, 0x03a11021 }, /* Mic */
 { 0x12, 0xd5a30140 }, /* Builtin Mic */
 { 0x13, 0x411111f0 }, /* N/A */
 { 0x18, 0x411111f0 }, /* N/A */
 {}
};

/* Sound Blaster Z pin configs taken from Windows Driver */
static const struct hda_pintbl sbz_pincfgs[] = {
 { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
 { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
 { 0x0d, 0x014510f0 }, /* Digital Out */
 { 0x0e, 0x01c510f0 }, /* SPDIF In */
 { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */
 { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
 { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */
 { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
 { 0x13, 0x908700f0 }, /* What U Hear In*/
 { 0x18, 0x50d000f0 }, /* N/A */
 {}
};

/* Sound Blaster ZxR pin configs taken from Windows Driver */
static const struct hda_pintbl zxr_pincfgs[] = {
 { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */
 { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/
 { 0x0d, 0x014510f0 }, /* Digital Out */
 { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/
 { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */
 { 0x10, 0x01017111 }, /* Port D -- Center/LFE */
 { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */
 { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */
 { 0x13, 0x908700f0 }, /* What U Hear In*/
 { 0x18, 0x50d000f0 }, /* N/A */
 {}
};

/* Recon3D pin configs taken from Windows Driver */
static const struct hda_pintbl r3d_pincfgs[] = {
 { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
 { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
 { 0x0d, 0x014510f0 }, /* Digital Out */
 { 0x0e, 0x01c520f0 }, /* SPDIF In */
 { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
 { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
 { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
 { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
 { 0x13, 0x908700f0 }, /* What U Hear In*/
 { 0x18, 0x50d000f0 }, /* N/A */
 {}
};

/* Sound Blaster AE-5 pin configs taken from Windows Driver */
static const struct hda_pintbl ae5_pincfgs[] = {
 { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
 { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
 { 0x0d, 0x014510f0 }, /* Digital Out */
 { 0x0e, 0x01c510f0 }, /* SPDIF In */
 { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */
 { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
 { 0x11, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */
 { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
 { 0x13, 0x908700f0 }, /* What U Hear In*/
 { 0x18, 0x50d000f0 }, /* N/A */
 {}
};

/* Recon3D integrated pin configs taken from Windows Driver */
static const struct hda_pintbl r3di_pincfgs[] = {
 { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
 { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
 { 0x0d, 0x014510f0 }, /* Digital Out */
 { 0x0e, 0x41c520f0 }, /* SPDIF In */
 { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
 { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
 { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
 { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
 { 0x13, 0x908700f0 }, /* What U Hear In*/
 { 0x18, 0x500000f0 }, /* N/A */
 {}
};

static const struct hda_pintbl ae7_pincfgs[] = {
 { 0x0b, 0x01017010 },
 { 0x0c, 0x014510f0 },
 { 0x0d, 0x414510f0 },
 { 0x0e, 0x01c520f0 },
 { 0x0f, 0x01017114 },
 { 0x10, 0x01017011 },
 { 0x11, 0x018170ff },
 { 0x12, 0x01a170f0 },
 { 0x13, 0x908700f0 },
 { 0x18, 0x500000f0 },
 {}
};

static const struct hda_quirk ca0132_quirks[] = {
 SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4),
 SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
 SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
 SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
 SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
 SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
 SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ),
 SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
 SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
 SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
 SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
 SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI),
 SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI),
 SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI),
 SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D),
 SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D),
 SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5),
 SND_PCI_QUIRK(0x1102, 0x0191, "Sound Blaster AE-5 Plus", QUIRK_AE5),
 SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7),
 {}
};

static const struct hda_model_fixup ca0132_quirk_models[] = {
 { .id = QUIRK_ALIENWARE, .name = "alienware" },
 { .id = QUIRK_ALIENWARE_M17XR4, .name = "alienware-m17xr4" },
 { .id = QUIRK_SBZ, .name = "sbz" },
 { .id = QUIRK_ZXR, .name = "zxr" },
 { .id = QUIRK_ZXR_DBPRO, .name = "zxr-dbpro" },
 { .id = QUIRK_R3DI, .name = "r3di" },
 { .id = QUIRK_R3D, .name = "r3d" },
 { .id = QUIRK_AE5, .name = "ae5" },
 { .id = QUIRK_AE7, .name = "ae7" },
 {}
};

/* Output selection quirk info structures. */
#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3
#define MAX_QUIRK_SCP_SET_VALS 2
struct ca0132_alt_out_set_info {
 unsigned int dac2port; /* ParamID 0x0d value. */

 bool has_hda_gpio;
 char hda_gpio_pin;
 char hda_gpio_set;

 unsigned int mmio_gpio_count;
 char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS];
 char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS];

 unsigned int scp_cmds_count;
 unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS];
 unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS];
 unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS];

 bool has_chipio_write;
 unsigned int chipio_write_addr;
 unsigned int chipio_write_data;
};

struct ca0132_alt_out_set_quirk_data {
 int quirk_id;

 bool has_headphone_gain;
 bool is_ae_series;

 struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS];
};

static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = {
 { .quirk_id = QUIRK_R3DI,
   .has_headphone_gain = false,
   .is_ae_series       = false,
   .out_set_info = {
  /* Speakers. */
  { .dac2port         = 0x24,
    .has_hda_gpio     = true,
    .hda_gpio_pin     = 2,
    .hda_gpio_set     = 1,
    .mmio_gpio_count  = 0,
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  },
  /* Headphones. */
  { .dac2port         = 0x21,
    .has_hda_gpio     = true,
    .hda_gpio_pin     = 2,
    .hda_gpio_set     = 0,
    .mmio_gpio_count  = 0,
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  } },
 },
 { .quirk_id = QUIRK_R3D,
   .has_headphone_gain = false,
   .is_ae_series       = false,
   .out_set_info = {
  /* Speakers. */
  { .dac2port         = 0x24,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 1,
    .mmio_gpio_pin    = { 1 },
    .mmio_gpio_set    = { 1 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  },
  /* Headphones. */
  { .dac2port         = 0x21,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 1,
    .mmio_gpio_pin    = { 1 },
    .mmio_gpio_set    = { 0 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  } },
 },
 { .quirk_id = QUIRK_SBZ,
   .has_headphone_gain = false,
   .is_ae_series       = false,
   .out_set_info = {
  /* Speakers. */
  { .dac2port         = 0x18,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 3,
    .mmio_gpio_pin    = { 7, 4, 1 },
    .mmio_gpio_set    = { 0, 1, 1 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false, },
  /* Headphones. */
  { .dac2port         = 0x12,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 3,
    .mmio_gpio_pin    = { 7, 4, 1 },
    .mmio_gpio_set    = { 1, 1, 0 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  } },
 },
 { .quirk_id = QUIRK_ZXR,
   .has_headphone_gain = true,
   .is_ae_series       = false,
   .out_set_info = {
  /* Speakers. */
  { .dac2port         = 0x24,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 3,
    .mmio_gpio_pin    = { 2, 3, 5 },
    .mmio_gpio_set    = { 1, 1, 0 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  },
  /* Headphones. */
  { .dac2port         = 0x21,
    .has_hda_gpio     = false,
    .mmio_gpio_count  = 3,
    .mmio_gpio_pin    = { 2, 3, 5 },
    .mmio_gpio_set    = { 0, 1, 1 },
    .scp_cmds_count   = 0,
    .has_chipio_write = false,
  } },
 },
 { .quirk_id = QUIRK_AE5,
   .has_headphone_gain = true,
   .is_ae_series       = true,
   .out_set_info = {
  /* Speakers. */
  { .dac2port          = 0xa4,
    .has_hda_gpio      = false,
    .mmio_gpio_count   = 0,
    .scp_cmds_count    = 2,
    .scp_cmd_mid       = { 0x96, 0x96 },
    .scp_cmd_req       = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
      SPEAKER_TUNING_FRONT_RIGHT_INVERT },
    .scp_cmd_val       = { FLOAT_ZERO, FLOAT_ZERO },
    .has_chipio_write  = true,
    .chipio_write_addr = 0x0018b03c,
    .chipio_write_data = 0x00000012
  },
  /* Headphones. */
  { .dac2port          = 0xa1,
    .has_hda_gpio      = false,
    .mmio_gpio_count   = 0,
    .scp_cmds_count    = 2,
    .scp_cmd_mid       = { 0x96, 0x96 },
    .scp_cmd_req       = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
      SPEAKER_TUNING_FRONT_RIGHT_INVERT },
    .scp_cmd_val       = { FLOAT_ONE, FLOAT_ONE },
    .has_chipio_write  = true,
    .chipio_write_addr = 0x0018b03c,
    .chipio_write_data = 0x00000012
  } },
 },
 { .quirk_id = QUIRK_AE7,
   .has_headphone_gain = true,
   .is_ae_series       = true,
   .out_set_info = {
  /* Speakers. */
  { .dac2port          = 0x58,
    .has_hda_gpio      = false,
    .mmio_gpio_count   = 1,
    .mmio_gpio_pin     = { 0 },
    .mmio_gpio_set     = { 1 },
    .scp_cmds_count    = 2,
    .scp_cmd_mid       = { 0x96, 0x96 },
    .scp_cmd_req       = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
      SPEAKER_TUNING_FRONT_RIGHT_INVERT },
    .scp_cmd_val       = { FLOAT_ZERO, FLOAT_ZERO },
    .has_chipio_write  = true,
    .chipio_write_addr = 0x0018b03c,
    .chipio_write_data = 0x00000000
  },
  /* Headphones. */
  { .dac2port          = 0x58,
    .has_hda_gpio      = false,
    .mmio_gpio_count   = 1,
    .mmio_gpio_pin     = { 0 },
    .mmio_gpio_set     = { 1 },
    .scp_cmds_count    = 2,
    .scp_cmd_mid       = { 0x96, 0x96 },
    .scp_cmd_req       = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
      SPEAKER_TUNING_FRONT_RIGHT_INVERT },
    .scp_cmd_val       = { FLOAT_ONE, FLOAT_ONE },
    .has_chipio_write  = true,
    .chipio_write_addr = 0x0018b03c,
    .chipio_write_data = 0x00000010
  } },
 }
};

/*
 * CA0132 codec access
 */

static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
  unsigned int verb, unsigned int parm, unsigned int *res)
{
 unsigned int response;
 response = snd_hda_codec_read(codec, nid, 0, verb, parm);
 *res = response;

 return ((response == -1) ? -1 : 0);
}

static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
  unsigned short converter_format, unsigned int *res)
{
 return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
    converter_format & 0xffff, res);
}

static int codec_set_converter_stream_channel(struct hda_codec *codec,
    hda_nid_t nid, unsigned char stream,
    unsigned char channel, unsigned int *res)
{
 unsigned char converter_stream_channel = 0;

 converter_stream_channel = (stream << 4) | (channel & 0x0f);
 return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
    converter_stream_channel, res);
}

/* Chip access helper function */
static int chipio_send(struct hda_codec *codec,
         unsigned int reg,
         unsigned int data)
{
 unsigned int res;
 unsigned long timeout = jiffies + msecs_to_jiffies(1000);

 /* send bits of data specified by reg */
 do {
  res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
      reg, data);
  if (res == VENDOR_STATUS_CHIPIO_OK)
   return 0;
  msleep(20);
 } while (time_before(jiffies, timeout));

 return -EIO;
}

/*
 * Write chip address through the vendor widget -- NOT protected by the Mutex!
 */

static int chipio_write_address(struct hda_codec *codec,
    unsigned int chip_addx)
{
 struct ca0132_spec *spec = codec->spec;
 int res;

 if (spec->curr_chip_addx == chip_addx)
   return 0;

 /* send low 16 bits of the address */
 res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
     chip_addx & 0xffff);

 if (res != -EIO) {
  /* send high 16 bits of the address */
  res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
      chip_addx >> 16);
 }

 spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx;

 return res;
}

/*
 * Write data through the vendor widget -- NOT protected by the Mutex!
 */

static int chipio_write_data(struct hda_codec *codec, unsigned int data)
{
 struct ca0132_spec *spec = codec->spec;
 int res;

 /* send low 16 bits of the data */
 res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);

 if (res != -EIO) {
  /* send high 16 bits of the data */
  res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
      data >> 16);
 }

 /*If no error encountered, automatically increment the address
as per chip behaviour*/

 spec->curr_chip_addx = (res != -EIO) ?
     (spec->curr_chip_addx + 4) : ~0U;
 return res;
}

/*
 * Write multiple data through the vendor widget -- NOT protected by the Mutex!
 */

static int chipio_write_data_multiple(struct hda_codec *codec,
          const u32 *data,
          unsigned int count)
{
 int status = 0;

 if (data == NULL) {
  codec_dbg(codec, "chipio_write_data null ptr\n");
  return -EINVAL;
 }

 while ((count-- != 0) && (status == 0))
  status = chipio_write_data(codec, *data++);

 return status;
}


/*
 * Read data through the vendor widget -- NOT protected by the Mutex!
 */

static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
{
 struct ca0132_spec *spec = codec->spec;
 int res;

 /* post read */
 res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);

 if (res != -EIO) {
  /* read status */
  res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
 }

 if (res != -EIO) {
  /* read data */
  *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
        VENDOR_CHIPIO_HIC_READ_DATA,
        0);
 }

 /*If no error encountered, automatically increment the address
as per chip behaviour*/

 spec->curr_chip_addx = (res != -EIO) ?
     (spec->curr_chip_addx + 4) : ~0U;
 return res;
}

/*
 * Write given value to the given address through the chip I/O widget.
 * protected by the Mutex
 */

static int chipio_write(struct hda_codec *codec,
  unsigned int chip_addx, const unsigned int data)
{
 struct ca0132_spec *spec = codec->spec;
 int err;

 mutex_lock(&spec->chipio_mutex);

 /* write the address, and if successful proceed to write data */
 err = chipio_write_address(codec, chip_addx);
 if (err < 0)
  goto exit;

 err = chipio_write_data(codec, data);
 if (err < 0)
  goto exit;

exit:
 mutex_unlock(&spec->chipio_mutex);
 return err;
}

/*
 * Write given value to the given address through the chip I/O widget.
 * not protected by the Mutex
 */

static int chipio_write_no_mutex(struct hda_codec *codec,
  unsigned int chip_addx, const unsigned int data)
{
 int err;


 /* write the address, and if successful proceed to write data */
 err = chipio_write_address(codec, chip_addx);
 if (err < 0)
  goto exit;

 err = chipio_write_data(codec, data);
 if (err < 0)
  goto exit;

exit:
 return err;
}

/*
 * Write multiple values to the given address through the chip I/O widget.
 * protected by the Mutex
 */

static int chipio_write_multiple(struct hda_codec *codec,
     u32 chip_addx,
     const u32 *data,
     unsigned int count)
{
 struct ca0132_spec *spec = codec->spec;
 int status;

 mutex_lock(&spec->chipio_mutex);
 status = chipio_write_address(codec, chip_addx);
 if (status < 0)
  goto error;

 status = chipio_write_data_multiple(codec, data, count);
error:
 mutex_unlock(&spec->chipio_mutex);

 return status;
}

/*
 * Read the given address through the chip I/O widget
 * protected by the Mutex
 */

static int chipio_read(struct hda_codec *codec,
  unsigned int chip_addx, unsigned int *data)
{
 struct ca0132_spec *spec = codec->spec;
 int err;

 mutex_lock(&spec->chipio_mutex);

 /* write the address, and if successful proceed to write data */
 err = chipio_write_address(codec, chip_addx);
 if (err < 0)
  goto exit;

 err = chipio_read_data(codec, data);
 if (err < 0)
  goto exit;

exit:
 mutex_unlock(&spec->chipio_mutex);
 return err;
}

/*
 * Set chip control flags through the chip I/O widget.
 */

static void chipio_set_control_flag(struct hda_codec *codec,
        enum control_flag_id flag_id,
        bool flag_state)
{
 unsigned int val;
 unsigned int flag_bit;

 flag_bit = (flag_state ? 1 : 0);
 val = (flag_bit << 7) | (flag_id);
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_FLAG_SET, val);
}

/*
 * Set chip parameters through the chip I/O widget.
 */

static void chipio_set_control_param(struct hda_codec *codec,
  enum control_param_id param_id, int param_val)
{
 struct ca0132_spec *spec = codec->spec;
 int val;

 if ((param_id < 32) && (param_val < 8)) {
  val = (param_val << 5) | (param_id);
  snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
        VENDOR_CHIPIO_PARAM_SET, val);
 } else {
  mutex_lock(&spec->chipio_mutex);
  if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
   snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
         VENDOR_CHIPIO_PARAM_EX_ID_SET,
         param_id);
   snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
         VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
         param_val);
  }
  mutex_unlock(&spec->chipio_mutex);
 }
}

/*
 * Set chip parameters through the chip I/O widget. NO MUTEX.
 */

static void chipio_set_control_param_no_mutex(struct hda_codec *codec,
  enum control_param_id param_id, int param_val)
{
 int val;

 if ((param_id < 32) && (param_val < 8)) {
  val = (param_val << 5) | (param_id);
  snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
        VENDOR_CHIPIO_PARAM_SET, val);
 } else {
  if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
   snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
         VENDOR_CHIPIO_PARAM_EX_ID_SET,
         param_id);
   snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
         VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
         param_val);
  }
 }
}
/*
 * Connect stream to a source point, and then connect
 * that source point to a destination point.
 */

static void chipio_set_stream_source_dest(struct hda_codec *codec,
    int streamid, int source_point, int dest_point)
{
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_ID, streamid);
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point);
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point);
}

/*
 * Set number of channels in the selected stream.
 */

static void chipio_set_stream_channels(struct hda_codec *codec,
    int streamid, unsigned int channels)
{
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_ID, streamid);
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAMS_CHANNELS, channels);
}

/*
 * Enable/Disable audio stream.
 */

static void chipio_set_stream_control(struct hda_codec *codec,
    int streamid, int enable)
{
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_ID, streamid);
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_CONTROL, enable);
}

/*
 * Get ChipIO audio stream's status.
 */

static void chipio_get_stream_control(struct hda_codec *codec,
    int streamid, unsigned int *enable)
{
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_STREAM_ID, streamid);
 *enable = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
      VENDOR_CHIPIO_PARAM_GET,
      CONTROL_PARAM_STREAM_CONTROL);
}

/*
 * Set sampling rate of the connection point. NO MUTEX.
 */

static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec,
    int connid, enum ca0132_sample_rate rate)
{
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_CONN_POINT_ID, connid);
 chipio_set_control_param_no_mutex(codec,
   CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate);
}

/*
 * Set sampling rate of the connection point.
 */

static void chipio_set_conn_rate(struct hda_codec *codec,
    int connid, enum ca0132_sample_rate rate)
{
 chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
 chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
     rate);
}

/*
 * Writes to the 8051's internal address space directly instead of indirectly,
 * giving access to the special function registers located at addresses
 * 0x80-0xFF.
 */

static void chipio_8051_write_direct(struct hda_codec *codec,
  unsigned int addr, unsigned int data)
{
 unsigned int verb;

 verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data;
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr);
}

/*
 * Writes to the 8051's exram, which has 16-bits of address space.
 * Data at addresses 0x2000-0x7fff is mirrored to 0x8000-0xdfff.
 * Data at 0x8000-0xdfff can also be used as program memory for the 8051 by
 * setting the pmem bank selection SFR.
 * 0xe000-0xffff is always mapped as program memory, with only 0xf000-0xffff
 * being writable.
 */

static void chipio_8051_set_address(struct hda_codec *codec, unsigned int addr)
{
 unsigned int tmp;

 /* Lower 8-bits. */
 tmp = addr & 0xff;
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_8051_ADDRESS_LOW, tmp);

 /* Upper 8-bits. */
 tmp = (addr >> 8) & 0xff;
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_8051_ADDRESS_HIGH, tmp);
}

static void chipio_8051_set_data(struct hda_codec *codec, unsigned int data)
{
 /* 8-bits of data. */
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_8051_DATA_WRITE, data & 0xff);
}

static unsigned int chipio_8051_get_data(struct hda_codec *codec)
{
 return snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_8051_DATA_READ, 0);
}

/* PLL_PMU writes share the lower address register of the 8051 exram writes. */
static void chipio_8051_set_data_pll(struct hda_codec *codec, unsigned int data)
{
 /* 8-bits of data. */
 snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
       VENDOR_CHIPIO_PLL_PMU_WRITE, data & 0xff);
}

static void chipio_8051_write_exram(struct hda_codec *codec,
  unsigned int addr, unsigned int data)
{
 struct ca0132_spec *spec = codec->spec;

 mutex_lock(&spec->chipio_mutex);

 chipio_8051_set_address(codec, addr);
 chipio_8051_set_data(codec, data);

 mutex_unlock(&spec->chipio_mutex);
}

static void chipio_8051_write_exram_no_mutex(struct hda_codec *codec,
  unsigned int addr, unsigned int data)
{
 chipio_8051_set_address(codec, addr);
 chipio_8051_set_data(codec, data);
}

/* Readback data from the 8051's exram. No mutex. */
static void chipio_8051_read_exram(struct hda_codec *codec,
  unsigned int addr, unsigned int *data)
{
 chipio_8051_set_address(codec, addr);
 *data = chipio_8051_get_data(codec);
}

static void chipio_8051_write_pll_pmu(struct hda_codec *codec,
  unsigned int addr, unsigned int data)
{
 struct ca0132_spec *spec = codec->spec;

 mutex_lock(&spec->chipio_mutex);

 chipio_8051_set_address(codec, addr & 0xff);
 chipio_8051_set_data_pll(codec, data);

 mutex_unlock(&spec->chipio_mutex);
}

static void chipio_8051_write_pll_pmu_no_mutex(struct hda_codec *codec,
  unsigned int addr, unsigned int data)
{
 chipio_8051_set_address(codec, addr & 0xff);
 chipio_8051_set_data_pll(codec, data);
}

/*
 * Enable clocks.
 */

static void chipio_enable_clocks(struct hda_codec *codec)
{
 struct ca0132_spec *spec = codec->spec;

 mutex_lock(&spec->chipio_mutex);

 chipio_8051_write_pll_pmu_no_mutex(codec, 0x00, 0xff);
 chipio_8051_write_pll_pmu_no_mutex(codec, 0x05, 0x0b);
 chipio_8051_write_pll_pmu_no_mutex(codec, 0x06, 0xff);

 mutex_unlock(&spec->chipio_mutex);
}

/*
 * CA0132 DSP IO stuffs
 */

static int dspio_send(struct hda_codec *codec, unsigned int reg,
        unsigned int data)
{
 int res;
 unsigned long timeout = jiffies + msecs_to_jiffies(1000);

 /* send bits of data specified by reg to dsp */
 do {
  res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
  if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
   return res;
  msleep(20);
 } while (time_before(jiffies, timeout));

 return -EIO;
}

/*
 * Wait for DSP to be ready for commands
 */

static void dspio_write_wait(struct hda_codec *codec)
{
 int status;
 unsigned long timeout = jiffies + msecs_to_jiffies(1000);

 do {
  status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
      VENDOR_DSPIO_STATUS, 0);
  if ((status == VENDOR_STATUS_DSPIO_OK) ||
      (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
   break;
  msleep(1);
 } while (time_before(jiffies, timeout));
}

/*
 * Write SCP data to DSP
 */

static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
{
 struct ca0132_spec *spec = codec->spec;
 int status;

 dspio_write_wait(codec);

 mutex_lock(&spec->chipio_mutex);
 status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
       scp_data & 0xffff);
 if (status < 0)
  goto error;

 status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
        scp_data >> 16);
 if (status < 0)
  goto error;

 /* OK, now check if the write itself has executed*/
 status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
        VENDOR_DSPIO_STATUS, 0);
error:
 mutex_unlock(&spec->chipio_mutex);

 return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
   -EIO : 0;
}

/*
 * Write multiple SCP data to DSP
 */

static int dspio_write_multiple(struct hda_codec *codec,
    unsigned int *buffer, unsigned int size)
{
 int status = 0;
 unsigned int count;

 if (buffer == NULL)
  return -EINVAL;

 count = 0;
 while (count < size) {
  status = dspio_write(codec, *buffer++);
  if (status != 0)
   break;
  count++;
 }

 return status;
}

static int dspio_read(struct hda_codec *codec, unsigned int *data)
{
 int status;

 status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
 if (status == -EIO)
  return status;

 status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
 if (status == -EIO ||
     status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)
  return -EIO;

 *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
       VENDOR_DSPIO_SCP_READ_DATA, 0);

 return 0;
}

static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
          unsigned int *buf_size, unsigned int size_count)
{
 int status = 0;
 unsigned int size = *buf_size;
 unsigned int count;
 unsigned int skip_count;
 unsigned int dummy;

 if (buffer == NULL)
  return -1;

 count = 0;
 while (count < size && count < size_count) {
  status = dspio_read(codec, buffer++);
  if (status != 0)
   break;
  count++;
 }

 skip_count = count;
 if (status == 0) {
  while (skip_count < size) {
   status = dspio_read(codec, &dummy);
   if (status != 0)
    break;
   skip_count++;
  }
 }
 *buf_size = count;

 return status;
}

/*
 * Construct the SCP header using corresponding fields
 */

static inline unsigned int
make_scp_header(unsigned int target_id, unsigned int source_id,
  unsigned int get_flag, unsigned int req,
  unsigned int device_flag, unsigned int resp_flag,
  unsigned int error_flag, unsigned int data_size)
{
 unsigned int header = 0;

 header = (data_size & 0x1f) << 27;
 header |= (error_flag & 0x01) << 26;
 header |= (resp_flag & 0x01) << 25;
 header |= (device_flag & 0x01) << 24;
 header |= (req & 0x7f) << 17;
 header |= (get_flag & 0x01) << 16;
 header |= (source_id & 0xff) << 8;
 header |= target_id & 0xff;

 return header;
}

/*
 * Extract corresponding fields from SCP header
 */

static inline void
extract_scp_header(unsigned int header,
     unsigned int *target_id, unsigned int *source_id,
     unsigned int *get_flag, unsigned int *req,
     unsigned int *device_flag, unsigned int *resp_flag,
     unsigned int *error_flag, unsigned int *data_size)
{
 if (data_size)
  *data_size = (header >> 27) & 0x1f;
 if (error_flag)
  *error_flag = (header >> 26) & 0x01;
 if (resp_flag)
  *resp_flag = (header >> 25) & 0x01;
 if (device_flag)
  *device_flag = (header >> 24) & 0x01;
 if (req)
  *req = (header >> 17) & 0x7f;
 if (get_flag)
  *get_flag = (header >> 16) & 0x01;
 if (source_id)
  *source_id = (header >> 8) & 0xff;
 if (target_id)
  *target_id = header & 0xff;
}

#define SCP_MAX_DATA_WORDS  (16)

/* Structure to contain any SCP message */
struct scp_msg {
 unsigned int hdr;
 unsigned int data[SCP_MAX_DATA_WORDS];
};

static void dspio_clear_response_queue(struct hda_codec *codec)
{
 unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 unsigned int dummy = 0;
 int status;

 /* clear all from the response queue */
 do {
  status = dspio_read(codec, &dummy);
 } while (status == 0 && time_before(jiffies, timeout));
}

static int dspio_get_response_data(struct hda_codec *codec)
{
 struct ca0132_spec *spec = codec->spec;
 unsigned int data = 0;
 unsigned int count;

 if (dspio_read(codec, &data) < 0)
  return -EIO;

 if ((data & 0x00ffffff) == spec->wait_scp_header) {
  spec->scp_resp_header = data;
  spec->scp_resp_count = data >> 27;
  count = spec->wait_num_data;
  dspio_read_multiple(codec, spec->scp_resp_data,
        &spec->scp_resp_count, count);
  return 0;
 }

 return -EIO;
}

/*
 * Send SCP message to DSP
 */

static int dspio_send_scp_message(struct hda_codec *codec,
      unsigned char *send_buf,
      unsigned int send_buf_size,
      unsigned char *return_buf,
      unsigned int return_buf_size,
      unsigned int *bytes_returned)
{
 struct ca0132_spec *spec = codec->spec;
 int status;
 unsigned int scp_send_size = 0;
 unsigned int total_size;
 bool waiting_for_resp = false;
 unsigned int header;
 struct scp_msg *ret_msg;
 unsigned int resp_src_id, resp_target_id;
 unsigned int data_size, src_id, target_id, get_flag, device_flag;

 if (bytes_returned)
  *bytes_returned = 0;

 /* get scp header from buffer */
 header = *((unsigned int *)send_buf);
 extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
      &device_flag, NULL, NULL, &data_size);
 scp_send_size = data_size + 1;
 total_size = (scp_send_size * 4);

 if (send_buf_size < total_size)
  return -EINVAL;

 if (get_flag || device_flag) {
  if (!return_buf || return_buf_size < 4 || !bytes_returned)
   return -EINVAL;

  spec->wait_scp_header = *((unsigned int *)send_buf);

  /* swap source id with target id */
  resp_target_id = src_id;
  resp_src_id = target_id;
  spec->wait_scp_header &= 0xffff0000;
  spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
  spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
  spec->wait_scp = 1;
  waiting_for_resp = true;
 }

 status = dspio_write_multiple(codec, (unsigned int *)send_buf,
          scp_send_size);
 if (status < 0) {
  spec->wait_scp = 0;
  return status;
 }

 if (waiting_for_resp) {
  unsigned long timeout = jiffies + msecs_to_jiffies(1000);
  memset(return_buf, 0, return_buf_size);
  do {
   msleep(20);
  } while (spec->wait_scp && time_before(jiffies, timeout));
  waiting_for_resp = false;
  if (!spec->wait_scp) {
   ret_msg = (struct scp_msg *)return_buf;
   memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
   memcpy(&ret_msg->data, spec->scp_resp_data,
          spec->wait_num_data);
   *bytes_returned = (spec->scp_resp_count + 1) * 4;
   status = 0;
  } else {
   status = -EIO;
  }
  spec->wait_scp = 0;
 }

 return status;
}

/**
 * dspio_scp - Prepare and send the SCP message to DSP
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=94 G=93

¤ Dauer der Verarbeitung: 0.62 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge