1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Universal Interface for Intel High Definition Audio Codec
4 *
5 * HD audio interface patch for C-Media CMI9880
6 *
7 * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
8 */
9
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/module.h>
13#include <sound/core.h>
14#include <sound/hda_codec.h>
15#include "hda_local.h"
16#include "hda_auto_parser.h"
17#include "hda_jack.h"
18#include "hda_generic.h"
19
20struct cmi_spec {
21	struct hda_gen_spec gen;
22};
23
24/*
25 * stuff for auto-parser
26 */
27static const struct hda_codec_ops cmi_auto_patch_ops = {
28	.build_controls = snd_hda_gen_build_controls,
29	.build_pcms = snd_hda_gen_build_pcms,
30	.init = snd_hda_gen_init,
31	.free = snd_hda_gen_free,
32	.unsol_event = snd_hda_jack_unsol_event,
33};
34
35static int patch_cmi9880(struct hda_codec *codec)
36{
37	struct cmi_spec *spec;
38	struct auto_pin_cfg *cfg;
39	int err;
40
41	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
42	if (spec == NULL)
43		return -ENOMEM;
44
45	codec->spec = spec;
46	codec->patch_ops = cmi_auto_patch_ops;
47	cfg = &spec->gen.autocfg;
48	snd_hda_gen_spec_init(&spec->gen);
49
50	err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
51	if (err < 0)
52		goto error;
53	err = snd_hda_gen_parse_auto_config(codec, cfg);
54	if (err < 0)
55		goto error;
56
57	return 0;
58
59 error:
60	snd_hda_gen_free(codec);
61	return err;
62}
63
64static int patch_cmi8888(struct hda_codec *codec)
65{
66	struct cmi_spec *spec;
67	struct auto_pin_cfg *cfg;
68	int err;
69
70	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
71	if (!spec)
72		return -ENOMEM;
73
74	codec->spec = spec;
75	codec->patch_ops = cmi_auto_patch_ops;
76	cfg = &spec->gen.autocfg;
77	snd_hda_gen_spec_init(&spec->gen);
78
79	/* mask NID 0x10 from the playback volume selection;
80	 * it's a headphone boost volume handled manually below
81	 */
82	spec->gen.out_vol_mask = (1ULL << 0x10);
83
84	err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
85	if (err < 0)
86		goto error;
87	err = snd_hda_gen_parse_auto_config(codec, cfg);
88	if (err < 0)
89		goto error;
90
91	if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) ==
92	    AC_JACK_HP_OUT) {
93		static const struct snd_kcontrol_new amp_kctl =
94			HDA_CODEC_VOLUME("Headphone Amp Playback Volume",
95					 0x10, 0, HDA_OUTPUT);
96		if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &amp_kctl)) {
97			err = -ENOMEM;
98			goto error;
99		}
100	}
101
102	return 0;
103
104 error:
105	snd_hda_gen_free(codec);
106	return err;
107}
108
109/*
110 * patch entries
111 */
112static const struct hda_device_id snd_hda_id_cmedia[] = {
113	HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
114	HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
115	HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
116	{} /* terminator */
117};
118MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
119
120MODULE_LICENSE("GPL");
121MODULE_DESCRIPTION("C-Media HD-audio codec");
122
123static struct hda_codec_driver cmedia_driver = {
124	.id = snd_hda_id_cmedia,
125};
126
127module_hda_codec_driver(cmedia_driver);
128