blob: e2642ba88b2d951271804b005652bd69dae9785f [file] [log] [blame]
Takashi Iwaie5f14242009-07-01 18:11:44 +02001/*
2 * HD audio interface patch for Cirrus Logic CS420x chip
3 *
4 * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
5 *
6 * This driver is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This driver is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <linux/init.h>
Takashi Iwaie5f14242009-07-01 18:11:44 +020022#include <linux/slab.h>
23#include <linux/pci.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040024#include <linux/module.h>
Takashi Iwaie5f14242009-07-01 18:11:44 +020025#include <sound/core.h>
Takashi Iwai1077a022012-12-19 16:39:18 +010026#include <sound/tlv.h>
Takashi Iwaie5f14242009-07-01 18:11:44 +020027#include "hda_codec.h"
28#include "hda_local.h"
Takashi Iwai128bc4b2012-05-07 17:42:31 +020029#include "hda_auto_parser.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020030#include "hda_jack.h"
Takashi Iwai1077a022012-12-19 16:39:18 +010031#include "hda_generic.h"
Takashi Iwaie5f14242009-07-01 18:11:44 +020032
33/*
34 */
35
36struct cs_spec {
Takashi Iwai1077a022012-12-19 16:39:18 +010037 struct hda_gen_spec gen;
Takashi Iwaie5f14242009-07-01 18:11:44 +020038
Takashi Iwaied208252009-07-07 09:04:26 +020039 unsigned int gpio_mask;
40 unsigned int gpio_dir;
41 unsigned int gpio_data;
Takashi Iwai6dfeb7032011-11-22 20:00:31 +010042 unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
43 unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
Takashi Iwaied208252009-07-07 09:04:26 +020044
Tim Howe56487c22011-07-22 16:41:00 -050045 /* CS421x */
46 unsigned int spdif_detect:1;
Takashi Iwai1077a022012-12-19 16:39:18 +010047 unsigned int spdif_present:1;
Tim Howe56487c22011-07-22 16:41:00 -050048 unsigned int sense_b:1;
49 hda_nid_t vendor_nid;
Takashi Iwaie5f14242009-07-01 18:11:44 +020050};
51
Tim Howe56487c22011-07-22 16:41:00 -050052/* available models with CS420x */
Takashi Iwaia6bae202009-07-06 15:15:22 +020053enum {
Vince Weaver4e7d7c62010-09-22 17:31:37 -040054 CS420X_MBP53,
Takashi Iwaia6bae202009-07-06 15:15:22 +020055 CS420X_MBP55,
Rafael Avila de Espindola1a5ba2e2009-12-22 07:59:37 +010056 CS420X_IMAC27,
Takashi Iwaib35aabd2012-08-24 19:09:45 +020057 CS420X_GPIO_13,
58 CS420X_GPIO_23,
Takashi Iwaief596a52012-09-11 16:53:08 +020059 CS420X_MBP101,
Takashi Iwaiffe4d12b2012-11-23 08:34:13 +010060 CS420X_MBP81,
Takashi Iwai6ab982e2013-06-17 10:19:49 +020061 CS420X_MBA42,
Takashi Iwaia6bae202009-07-06 15:15:22 +020062 CS420X_AUTO,
Takashi Iwai03efce72012-09-13 09:56:57 +020063 /* aliases */
64 CS420X_IMAC27_122 = CS420X_GPIO_23,
65 CS420X_APPLE = CS420X_GPIO_13,
Takashi Iwaia6bae202009-07-06 15:15:22 +020066};
67
Tim Howe56487c22011-07-22 16:41:00 -050068/* CS421x boards */
69enum {
70 CS421X_CDB4210,
Takashi Iwaib35aabd2012-08-24 19:09:45 +020071 CS421X_SENSE_B,
Dylan Reid4af161072013-04-04 15:35:31 -070072 CS421X_STUMPY,
Tim Howe56487c22011-07-22 16:41:00 -050073};
74
Takashi Iwai40c20fa2009-07-06 13:00:57 +020075/* Vendor-specific processing widget */
76#define CS420X_VENDOR_NID 0x11
77#define CS_DIG_OUT1_PIN_NID 0x10
78#define CS_DIG_OUT2_PIN_NID 0x15
Daniel J Blueman16337e02012-11-04 13:19:03 +080079#define CS_DMIC1_PIN_NID 0x0e
80#define CS_DMIC2_PIN_NID 0x12
Takashi Iwai40c20fa2009-07-06 13:00:57 +020081
82/* coef indices */
83#define IDX_SPDIF_STAT 0x0000
84#define IDX_SPDIF_CTL 0x0001
85#define IDX_ADC_CFG 0x0002
86/* SZC bitmask, 4 modes below:
87 * 0 = immediate,
88 * 1 = digital immediate, analog zero-cross
89 * 2 = digtail & analog soft-ramp
90 * 3 = digital soft-ramp, analog zero-cross
91 */
92#define CS_COEF_ADC_SZC_MASK (3 << 0)
93#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
94#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
95/* PGA mode: 0 = differential, 1 = signle-ended */
96#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
97#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
98#define IDX_DAC_CFG 0x0003
99/* SZC bitmask, 4 modes below:
100 * 0 = Immediate
101 * 1 = zero-cross
102 * 2 = soft-ramp
103 * 3 = soft-ramp on zero-cross
104 */
105#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
106#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
107#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
108
109#define IDX_BEEP_CFG 0x0004
110/* 0x0008 - test reg key */
111/* 0x0009 - 0x0014 -> 12 test regs */
112/* 0x0015 - visibility reg */
113
Tim Howe56487c22011-07-22 16:41:00 -0500114/*
115 * Cirrus Logic CS4210
116 *
117 * 1 DAC => HP(sense) / Speakers,
118 * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
119 * 1 SPDIF OUT => SPDIF Trasmitter(sense)
120*/
121#define CS4210_DAC_NID 0x02
122#define CS4210_ADC_NID 0x03
David Henningsson5660ffd2012-01-02 12:40:17 +0100123#define CS4210_VENDOR_NID 0x0B
Tim Howe56487c22011-07-22 16:41:00 -0500124#define CS421X_DMIC_PIN_NID 0x09 /* Port E */
125#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
126
127#define CS421X_IDX_DEV_CFG 0x01
128#define CS421X_IDX_ADC_CFG 0x02
129#define CS421X_IDX_DAC_CFG 0x03
130#define CS421X_IDX_SPK_CTL 0x04
131
132#define SPDIF_EVENT 0x04
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200133
David Henningsson5660ffd2012-01-02 12:40:17 +0100134/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
135#define CS4213_VENDOR_NID 0x09
136
137
Takashi Iwai277a57c2009-07-08 12:42:08 +0200138static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200139{
Tim Howe56487c22011-07-22 16:41:00 -0500140 struct cs_spec *spec = codec->spec;
141 snd_hda_codec_write(codec, spec->vendor_nid, 0,
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200142 AC_VERB_SET_COEF_INDEX, idx);
Tim Howe56487c22011-07-22 16:41:00 -0500143 return snd_hda_codec_read(codec, spec->vendor_nid, 0,
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200144 AC_VERB_GET_PROC_COEF, 0);
145}
146
Takashi Iwai277a57c2009-07-08 12:42:08 +0200147static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
148 unsigned int coef)
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200149{
Tim Howe56487c22011-07-22 16:41:00 -0500150 struct cs_spec *spec = codec->spec;
151 snd_hda_codec_write(codec, spec->vendor_nid, 0,
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200152 AC_VERB_SET_COEF_INDEX, idx);
Tim Howe56487c22011-07-22 16:41:00 -0500153 snd_hda_codec_write(codec, spec->vendor_nid, 0,
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200154 AC_VERB_SET_PROC_COEF, coef);
155}
156
Takashi Iwai21a4dc42009-07-06 12:55:46 +0200157/*
158 * auto-mute and auto-mic switching
Tim Howe56487c22011-07-22 16:41:00 -0500159 * CS421x auto-output redirecting
160 * HP/SPK/SPDIF
Takashi Iwai21a4dc42009-07-06 12:55:46 +0200161 */
162
Takashi Iwai1077a022012-12-19 16:39:18 +0100163static void cs_automute(struct hda_codec *codec)
Takashi Iwaie5f14242009-07-01 18:11:44 +0200164{
165 struct cs_spec *spec = codec->spec;
Takashi Iwaie5f14242009-07-01 18:11:44 +0200166
Takashi Iwai1077a022012-12-19 16:39:18 +0100167 /* mute HPs if spdif jack (SENSE_B) is present */
168 spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
Tim Howe56487c22011-07-22 16:41:00 -0500169
Takashi Iwai1077a022012-12-19 16:39:18 +0100170 snd_hda_gen_update_outputs(codec);
Tim Howe56487c22011-07-22 16:41:00 -0500171
Takashi Iwai6dfeb7032011-11-22 20:00:31 +0100172 if (spec->gpio_eapd_hp) {
Takashi Iwai039eb752013-03-18 16:55:49 +0100173 spec->gpio_data = spec->gen.hp_jack_present ?
Takashi Iwai6dfeb7032011-11-22 20:00:31 +0100174 spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
Stelian Pop3a385162009-07-30 14:44:27 +0200175 snd_hda_codec_write(codec, 0x01, 0,
Takashi Iwai039eb752013-03-18 16:55:49 +0100176 AC_VERB_SET_GPIO_DATA, spec->gpio_data);
Stelian Pop3a385162009-07-30 14:44:27 +0200177 }
Takashi Iwaie5f14242009-07-01 18:11:44 +0200178}
179
Takashi Iwai1077a022012-12-19 16:39:18 +0100180static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
Takashi Iwaie5f14242009-07-01 18:11:44 +0200181{
Takashi Iwai1077a022012-12-19 16:39:18 +0100182 unsigned int val;
183 val = snd_hda_codec_get_pincfg(codec, nid);
184 return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200185}
186
Takashi Iwai1077a022012-12-19 16:39:18 +0100187static void init_input_coef(struct hda_codec *codec)
Takashi Iwaie5f14242009-07-01 18:11:44 +0200188{
189 struct cs_spec *spec = codec->spec;
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200190 unsigned int coef;
Takashi Iwaie5f14242009-07-01 18:11:44 +0200191
David Henningsson5660ffd2012-01-02 12:40:17 +0100192 /* CS420x has multiple ADC, CS421x has single ADC */
193 if (spec->vendor_nid == CS420X_VENDOR_NID) {
Daniel J Blueman16337e02012-11-04 13:19:03 +0800194 coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
Tim Howe56487c22011-07-22 16:41:00 -0500195 if (is_active_pin(codec, CS_DMIC2_PIN_NID))
Daniel J Blueman16337e02012-11-04 13:19:03 +0800196 coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
Tim Howe56487c22011-07-22 16:41:00 -0500197 if (is_active_pin(codec, CS_DMIC1_PIN_NID))
Daniel J Blueman16337e02012-11-04 13:19:03 +0800198 coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off
Tim Howe56487c22011-07-22 16:41:00 -0500199 * No effect if SPDIF_OUT2 is
200 * selected in IDX_SPDIF_CTL.
201 */
Daniel J Blueman16337e02012-11-04 13:19:03 +0800202
203 cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
Tim Howe56487c22011-07-22 16:41:00 -0500204 }
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200205}
206
Takashi Iwaic42d4782011-05-02 11:36:09 +0200207static const struct hda_verb cs_coef_init_verbs[] = {
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200208 {0x11, AC_VERB_SET_PROC_STATE, 1},
209 {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
210 {0x11, AC_VERB_SET_PROC_COEF,
211 (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
212 | 0x0040 /* Mute DACs on FIFO error */
213 | 0x1000 /* Enable DACs High Pass Filter */
214 | 0x0400 /* Disable Coefficient Auto increment */
215 )},
Takashi Iwai829e87e2012-11-23 08:25:25 +0100216 /* ADC1/2 - Digital and Analog Soft Ramp */
217 {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
218 {0x11, AC_VERB_SET_PROC_COEF, 0x000a},
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200219 /* Beep */
Alexander Stein5a83b4b2012-11-01 13:42:37 +0100220 {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200221 {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
222
223 {} /* terminator */
224};
225
Brian Austina769cbc2010-09-07 14:36:22 -0500226/* Errata: CS4207 rev C0/C1/C2 Silicon
227 *
228 * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
229 *
230 * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
231 * may be excessive (up to an additional 200 μA), which is most easily
232 * observed while the part is being held in reset (RESET# active low).
233 *
234 * Root Cause: At initial powerup of the device, the logic that drives
235 * the clock and write enable to the S/PDIF SRC RAMs is not properly
236 * initialized.
237 * Certain random patterns will cause a steady leakage current in those
238 * RAM cells. The issue will resolve once the SRCs are used (turned on).
239 *
240 * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
241 * blocks, which will alleviate the issue.
242 */
243
Takashi Iwaic42d4782011-05-02 11:36:09 +0200244static const struct hda_verb cs_errata_init_verbs[] = {
Brian Austina769cbc2010-09-07 14:36:22 -0500245 {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
246 {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
247
248 {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
249 {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
250 {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
251 {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
252 {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
253 {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
254
255 {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
256 {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
257
258 {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
259 {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
260 {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
261 {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
262 {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
263 {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
264 {0x11, AC_VERB_SET_PROC_STATE, 0x00},
265
Takashi Iwai38c07642011-03-03 14:54:19 +0100266#if 0 /* Don't to set to D3 as we are in power-up sequence */
Brian Austina769cbc2010-09-07 14:36:22 -0500267 {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
268 {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
269 /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
Takashi Iwai38c07642011-03-03 14:54:19 +0100270#endif
Brian Austina769cbc2010-09-07 14:36:22 -0500271
272 {} /* terminator */
273};
274
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200275/* SPDIF setup */
Takashi Iwai1077a022012-12-19 16:39:18 +0100276static void init_digital_coef(struct hda_codec *codec)
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200277{
278 unsigned int coef;
279
280 coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
281 coef |= 0x0008; /* Replace with mute on error */
282 if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
283 coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
284 * SPDIF_OUT2 is shared with GPIO1 and
285 * DMIC_SDA2.
286 */
287 cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200288}
289
290static int cs_init(struct hda_codec *codec)
291{
292 struct cs_spec *spec = codec->spec;
293
Brian Austina769cbc2010-09-07 14:36:22 -0500294 /* init_verb sequence for C0/C1/C2 errata*/
295 snd_hda_sequence_write(codec, cs_errata_init_verbs);
296
Takashi Iwai40c20fa2009-07-06 13:00:57 +0200297 snd_hda_sequence_write(codec, cs_coef_init_verbs);
Takashi Iwaied208252009-07-07 09:04:26 +0200298
Takashi Iwai1077a022012-12-19 16:39:18 +0100299 snd_hda_gen_init(codec);
Takashi Iwai98415ea2012-11-23 08:26:58 +0100300
Takashi Iwaied208252009-07-07 09:04:26 +0200301 if (spec->gpio_mask) {
302 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
303 spec->gpio_mask);
304 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
305 spec->gpio_dir);
306 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
307 spec->gpio_data);
308 }
309
Takashi Iwai1077a022012-12-19 16:39:18 +0100310 init_input_coef(codec);
311 init_digital_coef(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +0200312
Takashi Iwaie5f14242009-07-01 18:11:44 +0200313 return 0;
314}
315
Takashi Iwai1077a022012-12-19 16:39:18 +0100316#define cs_free snd_hda_gen_free
Takashi Iwaie5f14242009-07-01 18:11:44 +0200317
Takashi Iwaic42d4782011-05-02 11:36:09 +0200318static const struct hda_codec_ops cs_patch_ops = {
Takashi Iwai1077a022012-12-19 16:39:18 +0100319 .build_controls = snd_hda_gen_build_controls,
320 .build_pcms = snd_hda_gen_build_pcms,
Takashi Iwaie5f14242009-07-01 18:11:44 +0200321 .init = cs_init,
322 .free = cs_free,
David Henningsson5c2e4e02012-10-08 15:44:15 +0200323 .unsol_event = snd_hda_jack_unsol_event,
Takashi Iwaie5f14242009-07-01 18:11:44 +0200324};
325
326static int cs_parse_auto_config(struct hda_codec *codec)
327{
328 struct cs_spec *spec = codec->spec;
329 int err;
330
Takashi Iwai1077a022012-12-19 16:39:18 +0100331 err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200332 if (err < 0)
333 return err;
Takashi Iwaied208252009-07-07 09:04:26 +0200334
Takashi Iwai1077a022012-12-19 16:39:18 +0100335 err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
Takashi Iwaied208252009-07-07 09:04:26 +0200336 if (err < 0)
337 return err;
Takashi Iwai1077a022012-12-19 16:39:18 +0100338
Takashi Iwaie5f14242009-07-01 18:11:44 +0200339 return 0;
340}
341
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200342static const struct hda_model_fixup cs420x_models[] = {
343 { .id = CS420X_MBP53, .name = "mbp53" },
344 { .id = CS420X_MBP55, .name = "mbp55" },
345 { .id = CS420X_IMAC27, .name = "imac27" },
346 { .id = CS420X_IMAC27_122, .name = "imac27_122" },
347 { .id = CS420X_APPLE, .name = "apple" },
Takashi Iwaief596a52012-09-11 16:53:08 +0200348 { .id = CS420X_MBP101, .name = "mbp101" },
Takashi Iwaiffe4d12b2012-11-23 08:34:13 +0100349 { .id = CS420X_MBP81, .name = "mbp81" },
Takashi Iwai6ab982e2013-06-17 10:19:49 +0200350 { .id = CS420X_MBA42, .name = "mba42" },
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200351 {}
Takashi Iwaia6bae202009-07-06 15:15:22 +0200352};
353
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200354static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
Vince Weaver4e7d7c62010-09-22 17:31:37 -0400355 SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
Edgar (gimli) Hucek87232dd2010-11-03 08:14:10 +0100356 SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
Takashi Iwaia6bae202009-07-06 15:15:22 +0200357 SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
Takashi Iwaif46119b2010-10-11 14:46:35 +0200358 SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
Takashi Iwai6dfeb7032011-11-22 20:00:31 +0100359 /* this conflicts with too many other models */
360 /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
Takashi Iwai6dfeb7032011-11-22 20:00:31 +0100361
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200362 /* codec SSID */
Takashi Iwaiffe4d12b2012-11-23 08:34:13 +0100363 SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
Jérémy Lal7e5bea12012-01-09 17:19:45 +0100364 SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
Takashi Iwaief596a52012-09-11 16:53:08 +0200365 SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
Takashi Iwaiccd2ddc2015-03-12 20:47:15 +0100366 SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81),
Takashi Iwai6ab982e2013-06-17 10:19:49 +0200367 SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
Takashi Iwai6dfeb7032011-11-22 20:00:31 +0100368 SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
Takashi Iwaia6bae202009-07-06 15:15:22 +0200369 {} /* terminator */
370};
371
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200372static const struct hda_pintbl mbp53_pincfgs[] = {
Vince Weaver4e7d7c62010-09-22 17:31:37 -0400373 { 0x09, 0x012b4050 },
374 { 0x0a, 0x90100141 },
375 { 0x0b, 0x90100140 },
376 { 0x0c, 0x018b3020 },
377 { 0x0d, 0x90a00110 },
378 { 0x0e, 0x400000f0 },
379 { 0x0f, 0x01cbe030 },
380 { 0x10, 0x014be060 },
381 { 0x12, 0x400000f0 },
382 { 0x15, 0x400000f0 },
383 {} /* terminator */
384};
385
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200386static const struct hda_pintbl mbp55_pincfgs[] = {
Takashi Iwaia6bae202009-07-06 15:15:22 +0200387 { 0x09, 0x012b4030 },
388 { 0x0a, 0x90100121 },
389 { 0x0b, 0x90100120 },
390 { 0x0c, 0x400000f0 },
391 { 0x0d, 0x90a00110 },
392 { 0x0e, 0x400000f0 },
393 { 0x0f, 0x400000f0 },
394 { 0x10, 0x014be040 },
395 { 0x12, 0x400000f0 },
396 { 0x15, 0x400000f0 },
397 {} /* terminator */
398};
399
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200400static const struct hda_pintbl imac27_pincfgs[] = {
Rafael Avila de Espindola1a5ba2e2009-12-22 07:59:37 +0100401 { 0x09, 0x012b4050 },
402 { 0x0a, 0x90100140 },
403 { 0x0b, 0x90100142 },
404 { 0x0c, 0x018b3020 },
405 { 0x0d, 0x90a00110 },
406 { 0x0e, 0x400000f0 },
407 { 0x0f, 0x01cbe030 },
408 { 0x10, 0x014be060 },
409 { 0x12, 0x01ab9070 },
410 { 0x15, 0x400000f0 },
411 {} /* terminator */
412};
413
Takashi Iwaief596a52012-09-11 16:53:08 +0200414static const struct hda_pintbl mbp101_pincfgs[] = {
415 { 0x0d, 0x40ab90f0 },
416 { 0x0e, 0x90a600f0 },
417 { 0x12, 0x50a600f0 },
418 {} /* terminator */
419};
420
Takashi Iwai6ab982e2013-06-17 10:19:49 +0200421static const struct hda_pintbl mba42_pincfgs[] = {
422 { 0x09, 0x012b4030 }, /* HP */
423 { 0x0a, 0x400000f0 },
424 { 0x0b, 0x90100120 }, /* speaker */
425 { 0x0c, 0x400000f0 },
426 { 0x0d, 0x90a00110 }, /* mic */
427 { 0x0e, 0x400000f0 },
428 { 0x0f, 0x400000f0 },
429 { 0x10, 0x400000f0 },
430 { 0x12, 0x400000f0 },
431 { 0x15, 0x400000f0 },
432 {} /* terminator */
433};
434
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200435static void cs420x_fixup_gpio_13(struct hda_codec *codec,
436 const struct hda_fixup *fix, int action)
Takashi Iwaia6bae202009-07-06 15:15:22 +0200437{
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200438 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
439 struct cs_spec *spec = codec->spec;
440 spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
441 spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
442 spec->gpio_mask = spec->gpio_dir =
443 spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
444 }
Takashi Iwaia6bae202009-07-06 15:15:22 +0200445}
446
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200447static void cs420x_fixup_gpio_23(struct hda_codec *codec,
448 const struct hda_fixup *fix, int action)
449{
450 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
451 struct cs_spec *spec = codec->spec;
452 spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
453 spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
454 spec->gpio_mask = spec->gpio_dir =
455 spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
456 }
457}
458
459static const struct hda_fixup cs420x_fixups[] = {
460 [CS420X_MBP53] = {
461 .type = HDA_FIXUP_PINS,
462 .v.pins = mbp53_pincfgs,
463 .chained = true,
464 .chain_id = CS420X_APPLE,
465 },
466 [CS420X_MBP55] = {
467 .type = HDA_FIXUP_PINS,
468 .v.pins = mbp55_pincfgs,
469 .chained = true,
470 .chain_id = CS420X_GPIO_13,
471 },
472 [CS420X_IMAC27] = {
473 .type = HDA_FIXUP_PINS,
474 .v.pins = imac27_pincfgs,
475 .chained = true,
476 .chain_id = CS420X_GPIO_13,
477 },
478 [CS420X_GPIO_13] = {
479 .type = HDA_FIXUP_FUNC,
480 .v.func = cs420x_fixup_gpio_13,
481 },
482 [CS420X_GPIO_23] = {
483 .type = HDA_FIXUP_FUNC,
484 .v.func = cs420x_fixup_gpio_23,
485 },
Takashi Iwaief596a52012-09-11 16:53:08 +0200486 [CS420X_MBP101] = {
487 .type = HDA_FIXUP_PINS,
488 .v.pins = mbp101_pincfgs,
489 .chained = true,
Takashi Iwaief596a52012-09-11 16:53:08 +0200490 .chain_id = CS420X_GPIO_13,
491 },
Takashi Iwaiffe4d12b2012-11-23 08:34:13 +0100492 [CS420X_MBP81] = {
493 .type = HDA_FIXUP_VERBS,
494 .v.verbs = (const struct hda_verb[]) {
495 /* internal mic ADC2: right only, single ended */
496 {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
497 {0x11, AC_VERB_SET_PROC_COEF, 0x102a},
498 {}
499 },
500 .chained = true,
501 .chain_id = CS420X_GPIO_13,
502 },
Takashi Iwai6ab982e2013-06-17 10:19:49 +0200503 [CS420X_MBA42] = {
504 .type = HDA_FIXUP_PINS,
505 .v.pins = mba42_pincfgs,
506 .chained = true,
507 .chain_id = CS420X_GPIO_13,
508 },
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200509};
510
Takashi Iwai1077a022012-12-19 16:39:18 +0100511static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
512{
513 struct cs_spec *spec;
514
515 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
516 if (!spec)
517 return NULL;
518 codec->spec = spec;
519 spec->vendor_nid = vendor_nid;
520 snd_hda_gen_spec_init(&spec->gen);
521
522 return spec;
523}
524
Takashi Iwaie5f14242009-07-01 18:11:44 +0200525static int patch_cs420x(struct hda_codec *codec)
526{
527 struct cs_spec *spec;
528 int err;
529
Takashi Iwai1077a022012-12-19 16:39:18 +0100530 spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200531 if (!spec)
532 return -ENOMEM;
Tim Howe56487c22011-07-22 16:41:00 -0500533
Takashi Iwai6d3073e2013-03-15 14:23:32 +0100534 spec->gen.automute_hook = cs_automute;
Takashi Iwaib4f97382015-03-12 20:28:04 +0100535 codec->single_adc_amp = 1;
Takashi Iwai6d3073e2013-03-15 14:23:32 +0100536
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200537 snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
538 cs420x_fixups);
539 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200540
Takashi Iwaied208252009-07-07 09:04:26 +0200541 err = cs_parse_auto_config(codec);
Takashi Iwai21a4dc42009-07-06 12:55:46 +0200542 if (err < 0)
543 goto error;
544
Takashi Iwaie5f14242009-07-01 18:11:44 +0200545 codec->patch_ops = cs_patch_ops;
546
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200547 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
548
Takashi Iwaie5f14242009-07-01 18:11:44 +0200549 return 0;
550
551 error:
Takashi Iwaic5e0b6d2012-10-10 08:50:35 +0200552 cs_free(codec);
Takashi Iwaie5f14242009-07-01 18:11:44 +0200553 return err;
554}
555
Tim Howe56487c22011-07-22 16:41:00 -0500556/*
557 * Cirrus Logic CS4210
558 *
559 * 1 DAC => HP(sense) / Speakers,
560 * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
561 * 1 SPDIF OUT => SPDIF Trasmitter(sense)
562*/
563
564/* CS4210 board names */
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200565static const struct hda_model_fixup cs421x_models[] = {
566 { .id = CS421X_CDB4210, .name = "cdb4210" },
Dylan Reid4af161072013-04-04 15:35:31 -0700567 { .id = CS421X_STUMPY, .name = "stumpy" },
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200568 {}
Tim Howe56487c22011-07-22 16:41:00 -0500569};
570
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200571static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
Tim Howe56487c22011-07-22 16:41:00 -0500572 /* Test Intel board + CDB2410 */
573 SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
574 {} /* terminator */
575};
576
577/* CS4210 board pinconfigs */
578/* Default CS4210 (CDB4210)*/
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200579static const struct hda_pintbl cdb4210_pincfgs[] = {
Tim Howe56487c22011-07-22 16:41:00 -0500580 { 0x05, 0x0321401f },
581 { 0x06, 0x90170010 },
582 { 0x07, 0x03813031 },
583 { 0x08, 0xb7a70037 },
584 { 0x09, 0xb7a6003e },
585 { 0x0a, 0x034510f0 },
586 {} /* terminator */
587};
588
Dylan Reid4af161072013-04-04 15:35:31 -0700589/* Stumpy ChromeBox */
590static const struct hda_pintbl stumpy_pincfgs[] = {
591 { 0x05, 0x022120f0 },
592 { 0x06, 0x901700f0 },
593 { 0x07, 0x02a120f0 },
594 { 0x08, 0x77a70037 },
595 { 0x09, 0x77a6003e },
596 { 0x0a, 0x434510f0 },
597 {} /* terminator */
598};
599
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200600/* Setup GPIO/SENSE for each board (if used) */
601static void cs421x_fixup_sense_b(struct hda_codec *codec,
602 const struct hda_fixup *fix, int action)
603{
604 struct cs_spec *spec = codec->spec;
605 if (action == HDA_FIXUP_ACT_PRE_PROBE)
606 spec->sense_b = 1;
607}
608
609static const struct hda_fixup cs421x_fixups[] = {
610 [CS421X_CDB4210] = {
611 .type = HDA_FIXUP_PINS,
612 .v.pins = cdb4210_pincfgs,
613 .chained = true,
614 .chain_id = CS421X_SENSE_B,
615 },
616 [CS421X_SENSE_B] = {
617 .type = HDA_FIXUP_FUNC,
618 .v.func = cs421x_fixup_sense_b,
Dylan Reid4af161072013-04-04 15:35:31 -0700619 },
620 [CS421X_STUMPY] = {
621 .type = HDA_FIXUP_PINS,
622 .v.pins = stumpy_pincfgs,
623 },
Tim Howe56487c22011-07-22 16:41:00 -0500624};
625
626static const struct hda_verb cs421x_coef_init_verbs[] = {
627 {0x0B, AC_VERB_SET_PROC_STATE, 1},
628 {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
629 /*
630 Disable Coefficient Index Auto-Increment(DAI)=1,
631 PDREF=0
632 */
633 {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
634
635 {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
636 /* ADC SZCMode = Digital Soft Ramp */
637 {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
638
639 {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
640 {0x0B, AC_VERB_SET_PROC_COEF,
641 (0x0002 /* DAC SZCMode = Digital Soft Ramp */
642 | 0x0004 /* Mute DAC on FIFO error */
643 | 0x0008 /* Enable DAC High Pass Filter */
644 )},
645 {} /* terminator */
646};
647
648/* Errata: CS4210 rev A1 Silicon
649 *
650 * http://www.cirrus.com/en/pubs/errata/
651 *
652 * Description:
653 * 1. Performance degredation is present in the ADC.
654 * 2. Speaker output is not completely muted upon HP detect.
655 * 3. Noise is present when clipping occurs on the amplified
656 * speaker outputs.
657 *
658 * Workaround:
659 * The following verb sequence written to the registers during
660 * initialization will correct the issues listed above.
661 */
662
663static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
664 {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
665
666 {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
667 {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
668
669 {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
670 {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
671
672 {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
673 {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
674
675 {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
676 {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
677
678 {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
679 {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
680
681 {} /* terminator */
682};
683
684/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
685static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
686
687static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
688 struct snd_ctl_elem_info *uinfo)
689{
690 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
691 uinfo->count = 1;
692 uinfo->value.integer.min = 0;
693 uinfo->value.integer.max = 3;
694 return 0;
695}
696
697static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
698 struct snd_ctl_elem_value *ucontrol)
699{
700 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
701
702 ucontrol->value.integer.value[0] =
703 cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
704 return 0;
705}
706
707static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
708 struct snd_ctl_elem_value *ucontrol)
709{
710 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
711
712 unsigned int vol = ucontrol->value.integer.value[0];
713 unsigned int coef =
714 cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
715 unsigned int original_coef = coef;
716
717 coef &= ~0x0003;
718 coef |= (vol & 0x0003);
719 if (original_coef == coef)
720 return 0;
721 else {
722 cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
723 return 1;
724 }
725}
726
Takashi Iwai1077a022012-12-19 16:39:18 +0100727static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
Tim Howe56487c22011-07-22 16:41:00 -0500728
729 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
730 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
731 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
732 .name = "Speaker Boost Playback Volume",
733 .info = cs421x_boost_vol_info,
734 .get = cs421x_boost_vol_get,
735 .put = cs421x_boost_vol_put,
736 .tlv = { .p = cs421x_speaker_boost_db_scale },
737};
738
David Henningsson5660ffd2012-01-02 12:40:17 +0100739static void cs4210_pinmux_init(struct hda_codec *codec)
Tim Howe56487c22011-07-22 16:41:00 -0500740{
741 struct cs_spec *spec = codec->spec;
742 unsigned int def_conf, coef;
743
744 /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
745 coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
746
747 if (spec->gpio_mask)
748 coef |= 0x0008; /* B1,B2 are GPIOs */
749 else
750 coef &= ~0x0008;
751
752 if (spec->sense_b)
753 coef |= 0x0010; /* B2 is SENSE_B, not inverted */
754 else
755 coef &= ~0x0010;
756
757 cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
758
759 if ((spec->gpio_mask || spec->sense_b) &&
760 is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
761
762 /*
763 GPIO or SENSE_B forced - disconnect the DMIC pin.
764 */
765 def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
766 def_conf &= ~AC_DEFCFG_PORT_CONN;
767 def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
768 snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
769 }
770}
771
Takashi Iwai1077a022012-12-19 16:39:18 +0100772static void cs4210_spdif_automute(struct hda_codec *codec,
773 struct hda_jack_tbl *tbl)
Tim Howe56487c22011-07-22 16:41:00 -0500774{
775 struct cs_spec *spec = codec->spec;
Takashi Iwai1077a022012-12-19 16:39:18 +0100776 bool spdif_present = false;
777 hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
Tim Howe56487c22011-07-22 16:41:00 -0500778
Takashi Iwai1077a022012-12-19 16:39:18 +0100779 /* detect on spdif is specific to CS4210 */
780 if (!spec->spdif_detect ||
781 spec->vendor_nid != CS4210_VENDOR_NID)
782 return;
783
784 spdif_present = snd_hda_jack_detect(codec, spdif_pin);
785 if (spdif_present == spec->spdif_present)
786 return;
787
788 spec->spdif_present = spdif_present;
789 /* SPDIF TX on/off */
790 if (spdif_present)
791 snd_hda_set_pin_ctl(codec, spdif_pin,
792 spdif_present ? PIN_OUT : 0);
793
794 cs_automute(codec);
795}
796
797static void parse_cs421x_digital(struct hda_codec *codec)
798{
799 struct cs_spec *spec = codec->spec;
800 struct auto_pin_cfg *cfg = &spec->gen.autocfg;
801 int i;
Tim Howe56487c22011-07-22 16:41:00 -0500802
803 for (i = 0; i < cfg->dig_outs; i++) {
804 hda_nid_t nid = cfg->dig_out_pins[i];
Tim Howe56487c22011-07-22 16:41:00 -0500805 if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
Tim Howe56487c22011-07-22 16:41:00 -0500806 spec->spdif_detect = 1;
Takashi Iwai1077a022012-12-19 16:39:18 +0100807 snd_hda_jack_detect_enable_callback(codec, nid,
808 SPDIF_EVENT,
809 cs4210_spdif_automute);
Tim Howe56487c22011-07-22 16:41:00 -0500810 }
811 }
812}
813
814static int cs421x_init(struct hda_codec *codec)
815{
816 struct cs_spec *spec = codec->spec;
817
David Henningsson5660ffd2012-01-02 12:40:17 +0100818 if (spec->vendor_nid == CS4210_VENDOR_NID) {
819 snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
820 snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
821 cs4210_pinmux_init(codec);
822 }
Tim Howe56487c22011-07-22 16:41:00 -0500823
Takashi Iwai1077a022012-12-19 16:39:18 +0100824 snd_hda_gen_init(codec);
825
Tim Howe56487c22011-07-22 16:41:00 -0500826 if (spec->gpio_mask) {
827 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
828 spec->gpio_mask);
829 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
830 spec->gpio_dir);
831 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
832 spec->gpio_data);
833 }
834
Takashi Iwai1077a022012-12-19 16:39:18 +0100835 init_input_coef(codec);
836
837 cs4210_spdif_automute(codec, NULL);
Tim Howe56487c22011-07-22 16:41:00 -0500838
839 return 0;
840}
841
Tim Howe56487c22011-07-22 16:41:00 -0500842static int cs421x_build_controls(struct hda_codec *codec)
843{
Takashi Iwai01a61e12011-10-28 00:03:22 +0200844 struct cs_spec *spec = codec->spec;
Tim Howe56487c22011-07-22 16:41:00 -0500845 int err;
846
Takashi Iwai1077a022012-12-19 16:39:18 +0100847 err = snd_hda_gen_build_controls(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +0200848 if (err < 0)
849 return err;
850
Takashi Iwai1077a022012-12-19 16:39:18 +0100851 if (spec->gen.autocfg.speaker_outs &&
852 spec->vendor_nid == CS4210_VENDOR_NID) {
853 err = snd_hda_ctl_add(codec, 0,
854 snd_ctl_new1(&cs421x_speaker_boost_ctl, codec));
855 if (err < 0)
856 return err;
857 }
Takashi Iwai01a61e12011-10-28 00:03:22 +0200858 return 0;
Tim Howe56487c22011-07-22 16:41:00 -0500859}
860
Takashi Iwai1077a022012-12-19 16:39:18 +0100861static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
Tim Howe56487c22011-07-22 16:41:00 -0500862{
Takashi Iwai1077a022012-12-19 16:39:18 +0100863 unsigned int caps;
Tim Howe56487c22011-07-22 16:41:00 -0500864
Takashi Iwai1077a022012-12-19 16:39:18 +0100865 /* set the upper-limit for mixer amp to 0dB */
866 caps = query_amp_caps(codec, dac, HDA_OUTPUT);
867 caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
868 caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
869 << AC_AMPCAP_NUM_STEPS_SHIFT;
870 snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
Tim Howe56487c22011-07-22 16:41:00 -0500871}
872
873static int cs421x_parse_auto_config(struct hda_codec *codec)
874{
875 struct cs_spec *spec = codec->spec;
Takashi Iwai1077a022012-12-19 16:39:18 +0100876 hda_nid_t dac = CS4210_DAC_NID;
Tim Howe56487c22011-07-22 16:41:00 -0500877 int err;
878
Takashi Iwai1077a022012-12-19 16:39:18 +0100879 fix_volume_caps(codec, dac);
880
881 err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
Tim Howe56487c22011-07-22 16:41:00 -0500882 if (err < 0)
883 return err;
Takashi Iwai1077a022012-12-19 16:39:18 +0100884
885 err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
Tim Howe56487c22011-07-22 16:41:00 -0500886 if (err < 0)
887 return err;
Takashi Iwai1077a022012-12-19 16:39:18 +0100888
889 parse_cs421x_digital(codec);
Tim Howe56487c22011-07-22 16:41:00 -0500890 return 0;
891}
892
893#ifdef CONFIG_PM
894/*
895 Manage PDREF, when transitioning to D3hot
896 (DAC,ADC) -> D3, PDREF=1, AFG->D3
897*/
Takashi Iwai68cb2b52012-07-02 15:20:37 +0200898static int cs421x_suspend(struct hda_codec *codec)
Tim Howe56487c22011-07-22 16:41:00 -0500899{
David Henningsson5660ffd2012-01-02 12:40:17 +0100900 struct cs_spec *spec = codec->spec;
Tim Howe56487c22011-07-22 16:41:00 -0500901 unsigned int coef;
902
903 snd_hda_shutup_pins(codec);
904
905 snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
906 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
907 snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
908 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
909
David Henningsson5660ffd2012-01-02 12:40:17 +0100910 if (spec->vendor_nid == CS4210_VENDOR_NID) {
911 coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
912 coef |= 0x0004; /* PDREF */
913 cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
914 }
Tim Howe56487c22011-07-22 16:41:00 -0500915
916 return 0;
917}
918#endif
919
Daniel J Blueman00e17f72012-11-04 13:19:04 +0800920static const struct hda_codec_ops cs421x_patch_ops = {
Tim Howe56487c22011-07-22 16:41:00 -0500921 .build_controls = cs421x_build_controls,
Takashi Iwai1077a022012-12-19 16:39:18 +0100922 .build_pcms = snd_hda_gen_build_pcms,
Tim Howe56487c22011-07-22 16:41:00 -0500923 .init = cs421x_init,
924 .free = cs_free,
David Henningsson5c2e4e02012-10-08 15:44:15 +0200925 .unsol_event = snd_hda_jack_unsol_event,
Tim Howe56487c22011-07-22 16:41:00 -0500926#ifdef CONFIG_PM
927 .suspend = cs421x_suspend,
928#endif
929};
930
David Henningsson5660ffd2012-01-02 12:40:17 +0100931static int patch_cs4210(struct hda_codec *codec)
Tim Howe56487c22011-07-22 16:41:00 -0500932{
933 struct cs_spec *spec;
934 int err;
935
Takashi Iwai1077a022012-12-19 16:39:18 +0100936 spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
Tim Howe56487c22011-07-22 16:41:00 -0500937 if (!spec)
938 return -ENOMEM;
Tim Howe56487c22011-07-22 16:41:00 -0500939
Takashi Iwai6d3073e2013-03-15 14:23:32 +0100940 spec->gen.automute_hook = cs_automute;
941
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200942 snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
943 cs421x_fixups);
944 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
Tim Howe56487c22011-07-22 16:41:00 -0500945
946 /*
947 Update the GPIO/DMIC/SENSE_B pinmux before the configuration
948 is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
949 is disabled.
950 */
David Henningsson5660ffd2012-01-02 12:40:17 +0100951 cs4210_pinmux_init(codec);
Tim Howe56487c22011-07-22 16:41:00 -0500952
953 err = cs421x_parse_auto_config(codec);
954 if (err < 0)
955 goto error;
956
David Henningsson5660ffd2012-01-02 12:40:17 +0100957 codec->patch_ops = cs421x_patch_ops;
Tim Howe56487c22011-07-22 16:41:00 -0500958
Takashi Iwaib35aabd2012-08-24 19:09:45 +0200959 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
960
Tim Howe56487c22011-07-22 16:41:00 -0500961 return 0;
962
963 error:
Takashi Iwaic5e0b6d2012-10-10 08:50:35 +0200964 cs_free(codec);
Tim Howe56487c22011-07-22 16:41:00 -0500965 return err;
966}
967
David Henningsson5660ffd2012-01-02 12:40:17 +0100968static int patch_cs4213(struct hda_codec *codec)
969{
970 struct cs_spec *spec;
971 int err;
972
Takashi Iwai1077a022012-12-19 16:39:18 +0100973 spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
David Henningsson5660ffd2012-01-02 12:40:17 +0100974 if (!spec)
975 return -ENOMEM;
David Henningsson5660ffd2012-01-02 12:40:17 +0100976
977 err = cs421x_parse_auto_config(codec);
978 if (err < 0)
979 goto error;
980
981 codec->patch_ops = cs421x_patch_ops;
982 return 0;
983
984 error:
Takashi Iwaic5e0b6d2012-10-10 08:50:35 +0200985 cs_free(codec);
David Henningsson5660ffd2012-01-02 12:40:17 +0100986 return err;
987}
988
Takashi Iwaie5f14242009-07-01 18:11:44 +0200989
990/*
991 * patch entries
992 */
Takashi Iwaic42d4782011-05-02 11:36:09 +0200993static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
Takashi Iwaie5f14242009-07-01 18:11:44 +0200994 { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
995 { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
David Henningsson5660ffd2012-01-02 12:40:17 +0100996 { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
997 { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
Takashi Iwaie5f14242009-07-01 18:11:44 +0200998 {} /* terminator */
999};
1000
1001MODULE_ALIAS("snd-hda-codec-id:10134206");
1002MODULE_ALIAS("snd-hda-codec-id:10134207");
Tim Howe56487c22011-07-22 16:41:00 -05001003MODULE_ALIAS("snd-hda-codec-id:10134210");
David Henningsson5660ffd2012-01-02 12:40:17 +01001004MODULE_ALIAS("snd-hda-codec-id:10134213");
Takashi Iwaie5f14242009-07-01 18:11:44 +02001005
1006MODULE_LICENSE("GPL");
1007MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
1008
1009static struct hda_codec_preset_list cirrus_list = {
1010 .preset = snd_hda_preset_cirrus,
1011 .owner = THIS_MODULE,
1012};
1013
1014static int __init patch_cirrus_init(void)
1015{
1016 return snd_hda_add_codec_preset(&cirrus_list);
1017}
1018
1019static void __exit patch_cirrus_exit(void)
1020{
1021 snd_hda_delete_codec_preset(&cirrus_list);
1022}
1023
1024module_init(patch_cirrus_init)
1025module_exit(patch_cirrus_exit)