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)) |