diff options
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
| -rw-r--r-- | sound/soc/codecs/wm_hubs.c | 83 |
1 files changed, 51 insertions, 32 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 0ad9f5d536c6..e1f225a3ac46 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c | |||
| @@ -62,21 +62,27 @@ static const char *speaker_mode_text[] = { | |||
| 62 | static const struct soc_enum speaker_mode = | 62 | static const struct soc_enum speaker_mode = |
| 63 | SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text); | 63 | SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text); |
| 64 | 64 | ||
| 65 | static void wait_for_dc_servo(struct snd_soc_codec *codec) | 65 | static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) |
| 66 | { | 66 | { |
| 67 | unsigned int reg; | 67 | unsigned int reg; |
| 68 | int count = 0; | 68 | int count = 0; |
| 69 | unsigned int val; | ||
| 70 | |||
| 71 | val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1; | ||
| 72 | |||
| 73 | /* Trigger the command */ | ||
| 74 | snd_soc_write(codec, WM8993_DC_SERVO_0, val); | ||
| 69 | 75 | ||
| 70 | dev_dbg(codec->dev, "Waiting for DC servo...\n"); | 76 | dev_dbg(codec->dev, "Waiting for DC servo...\n"); |
| 71 | 77 | ||
| 72 | do { | 78 | do { |
| 73 | count++; | 79 | count++; |
| 74 | msleep(1); | 80 | msleep(1); |
| 75 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); | 81 | reg = snd_soc_read(codec, WM8993_DC_SERVO_0); |
| 76 | dev_dbg(codec->dev, "DC servo: %x\n", reg); | 82 | dev_dbg(codec->dev, "DC servo: %x\n", reg); |
| 77 | } while (reg & WM8993_DCS_DATAPATH_BUSY); | 83 | } while (reg & op && count < 400); |
| 78 | 84 | ||
| 79 | if (reg & WM8993_DCS_DATAPATH_BUSY) | 85 | if (reg & op) |
| 80 | dev_err(codec->dev, "Timed out waiting for DC Servo\n"); | 86 | dev_err(codec->dev, "Timed out waiting for DC Servo\n"); |
| 81 | } | 87 | } |
| 82 | 88 | ||
| @@ -86,51 +92,58 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) | |||
| 86 | static void calibrate_dc_servo(struct snd_soc_codec *codec) | 92 | static void calibrate_dc_servo(struct snd_soc_codec *codec) |
| 87 | { | 93 | { |
| 88 | struct wm_hubs_data *hubs = codec->private_data; | 94 | struct wm_hubs_data *hubs = codec->private_data; |
| 89 | u16 reg, dcs_cfg; | 95 | u16 reg, reg_l, reg_r, dcs_cfg; |
| 90 | 96 | ||
| 91 | /* Set for 32 series updates */ | 97 | /* Set for 32 series updates */ |
| 92 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, | 98 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, |
| 93 | WM8993_DCS_SERIES_NO_01_MASK, | 99 | WM8993_DCS_SERIES_NO_01_MASK, |
| 94 | 32 << WM8993_DCS_SERIES_NO_01_SHIFT); | 100 | 32 << WM8993_DCS_SERIES_NO_01_SHIFT); |
| 95 | 101 | wait_for_dc_servo(codec, | |
| 96 | /* Enable the DC servo. Write all bits to avoid triggering startup | 102 | WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); |
| 97 | * or write calibration. | ||
| 98 | */ | ||
| 99 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | ||
| 100 | 0xFFFF, | ||
| 101 | WM8993_DCS_ENA_CHAN_0 | | ||
| 102 | WM8993_DCS_ENA_CHAN_1 | | ||
| 103 | WM8993_DCS_TRIG_SERIES_1 | | ||
| 104 | WM8993_DCS_TRIG_SERIES_0); | ||
| 105 | |||
| 106 | wait_for_dc_servo(codec); | ||
| 107 | 103 | ||
| 108 | /* Apply correction to DC servo result */ | 104 | /* Apply correction to DC servo result */ |
| 109 | if (hubs->dcs_codes) { | 105 | if (hubs->dcs_codes) { |
| 110 | dev_dbg(codec->dev, "Applying %d code DC servo correction\n", | 106 | dev_dbg(codec->dev, "Applying %d code DC servo correction\n", |
| 111 | hubs->dcs_codes); | 107 | hubs->dcs_codes); |
| 112 | 108 | ||
| 109 | /* Different chips in the family support different | ||
| 110 | * readback methods. | ||
| 111 | */ | ||
| 112 | switch (hubs->dcs_readback_mode) { | ||
| 113 | case 0: | ||
| 114 | reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) | ||
| 115 | & WM8993_DCS_INTEG_CHAN_0_MASK;; | ||
| 116 | reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) | ||
| 117 | & WM8993_DCS_INTEG_CHAN_1_MASK; | ||
| 118 | break; | ||
| 119 | case 1: | ||
| 120 | reg = snd_soc_read(codec, WM8993_DC_SERVO_3); | ||
| 121 | reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) | ||
| 122 | >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; | ||
| 123 | reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; | ||
| 124 | break; | ||
| 125 | default: | ||
| 126 | WARN(1, "Unknown DCS readback method"); | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | |||
| 113 | /* HPOUT1L */ | 130 | /* HPOUT1L */ |
| 114 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) & | 131 | if (reg_l + hubs->dcs_codes > 0 && |
| 115 | WM8993_DCS_INTEG_CHAN_0_MASK;; | 132 | reg_l + hubs->dcs_codes < 0xff) |
| 116 | reg += hubs->dcs_codes; | 133 | reg_l += hubs->dcs_codes; |
| 117 | dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT; | 134 | dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; |
| 118 | 135 | ||
| 119 | /* HPOUT1R */ | 136 | /* HPOUT1R */ |
| 120 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) & | 137 | if (reg_r + hubs->dcs_codes > 0 && |
| 121 | WM8993_DCS_INTEG_CHAN_1_MASK; | 138 | reg_r + hubs->dcs_codes < 0xff) |
| 122 | reg += hubs->dcs_codes; | 139 | reg_r += hubs->dcs_codes; |
| 123 | dcs_cfg |= reg; | 140 | dcs_cfg |= reg_r; |
| 124 | 141 | ||
| 125 | /* Do it */ | 142 | /* Do it */ |
| 126 | snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); | 143 | snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); |
| 127 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | 144 | wait_for_dc_servo(codec, |
| 128 | WM8993_DCS_TRIG_DAC_WR_0 | | 145 | WM8993_DCS_TRIG_DAC_WR_0 | |
| 129 | WM8993_DCS_TRIG_DAC_WR_1, | 146 | WM8993_DCS_TRIG_DAC_WR_1); |
| 130 | WM8993_DCS_TRIG_DAC_WR_0 | | ||
| 131 | WM8993_DCS_TRIG_DAC_WR_1); | ||
| 132 | |||
| 133 | wait_for_dc_servo(codec); | ||
| 134 | } | 147 | } |
| 135 | } | 148 | } |
| 136 | 149 | ||
| @@ -141,10 +154,16 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, | |||
| 141 | struct snd_ctl_elem_value *ucontrol) | 154 | struct snd_ctl_elem_value *ucontrol) |
| 142 | { | 155 | { |
| 143 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 156 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| 157 | struct wm_hubs_data *hubs = codec->private_data; | ||
| 144 | int ret; | 158 | int ret; |
| 145 | 159 | ||
| 146 | ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); | 160 | ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); |
| 147 | 161 | ||
| 162 | /* If we're applying an offset correction then updating the | ||
| 163 | * callibration would be likely to introduce further offsets. */ | ||
| 164 | if (hubs->dcs_codes) | ||
| 165 | return ret; | ||
| 166 | |||
| 148 | /* Only need to do this if the outputs are active */ | 167 | /* Only need to do this if the outputs are active */ |
| 149 | if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1) | 168 | if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1) |
| 150 | & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA)) | 169 | & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA)) |
