16014e902STakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later
26014e902STakashi Iwai /*
3*0f1e8306STakashi Iwai * Universal codec driver for Intel High Definition Audio Codec
46014e902STakashi Iwai *
5*0f1e8306STakashi Iwai * HD audio codec driver for C-Media CMI9880
66014e902STakashi Iwai *
76014e902STakashi Iwai * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
86014e902STakashi Iwai */
96014e902STakashi Iwai
106014e902STakashi Iwai #include <linux/init.h>
116014e902STakashi Iwai #include <linux/slab.h>
126014e902STakashi Iwai #include <linux/module.h>
136014e902STakashi Iwai #include <sound/core.h>
146014e902STakashi Iwai #include <sound/hda_codec.h>
156014e902STakashi Iwai #include "hda_local.h"
166014e902STakashi Iwai #include "hda_auto_parser.h"
176014e902STakashi Iwai #include "hda_jack.h"
186014e902STakashi Iwai #include "generic.h"
196014e902STakashi Iwai
cmedia_probe(struct hda_codec * codec,const struct hda_device_id * id)20*0f1e8306STakashi Iwai static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id)
216014e902STakashi Iwai {
22*0f1e8306STakashi Iwai struct hda_gen_spec *spec;
236014e902STakashi Iwai struct auto_pin_cfg *cfg;
24*0f1e8306STakashi Iwai bool is_cmi8888 = id->vendor_id == 0x13f68888;
256014e902STakashi Iwai int err;
266014e902STakashi Iwai
276014e902STakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL);
286014e902STakashi Iwai if (spec == NULL)
296014e902STakashi Iwai return -ENOMEM;
306014e902STakashi Iwai
316014e902STakashi Iwai codec->spec = spec;
32*0f1e8306STakashi Iwai cfg = &spec->autocfg;
33*0f1e8306STakashi Iwai snd_hda_gen_spec_init(spec);
346014e902STakashi Iwai
35*0f1e8306STakashi Iwai if (is_cmi8888) {
366014e902STakashi Iwai /* mask NID 0x10 from the playback volume selection;
376014e902STakashi Iwai * it's a headphone boost volume handled manually below
386014e902STakashi Iwai */
39*0f1e8306STakashi Iwai spec->out_vol_mask = (1ULL << 0x10);
40*0f1e8306STakashi Iwai }
416014e902STakashi Iwai
426014e902STakashi Iwai err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
436014e902STakashi Iwai if (err < 0)
446014e902STakashi Iwai goto error;
456014e902STakashi Iwai err = snd_hda_gen_parse_auto_config(codec, cfg);
466014e902STakashi Iwai if (err < 0)
476014e902STakashi Iwai goto error;
486014e902STakashi Iwai
49*0f1e8306STakashi Iwai err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
50*0f1e8306STakashi Iwai if (err < 0)
51*0f1e8306STakashi Iwai goto error;
52*0f1e8306STakashi Iwai err = snd_hda_gen_parse_auto_config(codec, cfg);
53*0f1e8306STakashi Iwai if (err < 0)
54*0f1e8306STakashi Iwai goto error;
55*0f1e8306STakashi Iwai
56*0f1e8306STakashi Iwai if (is_cmi8888) {
576014e902STakashi Iwai if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) ==
586014e902STakashi Iwai AC_JACK_HP_OUT) {
596014e902STakashi Iwai static const struct snd_kcontrol_new amp_kctl =
606014e902STakashi Iwai HDA_CODEC_VOLUME("Headphone Amp Playback Volume",
616014e902STakashi Iwai 0x10, 0, HDA_OUTPUT);
62*0f1e8306STakashi Iwai if (!snd_hda_gen_add_kctl(spec, NULL, &_kctl)) {
636014e902STakashi Iwai err = -ENOMEM;
646014e902STakashi Iwai goto error;
656014e902STakashi Iwai }
666014e902STakashi Iwai }
67*0f1e8306STakashi Iwai }
686014e902STakashi Iwai
696014e902STakashi Iwai return 0;
706014e902STakashi Iwai
716014e902STakashi Iwai error:
72*0f1e8306STakashi Iwai snd_hda_gen_remove(codec);
736014e902STakashi Iwai return err;
746014e902STakashi Iwai }
756014e902STakashi Iwai
76*0f1e8306STakashi Iwai static const struct hda_codec_ops cmedia_codec_ops = {
77*0f1e8306STakashi Iwai .probe = cmedia_probe,
78*0f1e8306STakashi Iwai .remove = snd_hda_gen_remove,
79*0f1e8306STakashi Iwai .build_controls = snd_hda_gen_build_controls,
80*0f1e8306STakashi Iwai .build_pcms = snd_hda_gen_build_pcms,
81*0f1e8306STakashi Iwai .init = snd_hda_gen_init,
82*0f1e8306STakashi Iwai .unsol_event = snd_hda_jack_unsol_event,
83*0f1e8306STakashi Iwai .check_power_status = snd_hda_gen_check_power_status,
84*0f1e8306STakashi Iwai .stream_pm = snd_hda_gen_stream_pm,
85*0f1e8306STakashi Iwai };
86*0f1e8306STakashi Iwai
876014e902STakashi Iwai /*
88*0f1e8306STakashi Iwai * driver entries
896014e902STakashi Iwai */
906014e902STakashi Iwai static const struct hda_device_id snd_hda_id_cmedia[] = {
91*0f1e8306STakashi Iwai HDA_CODEC_ID(0x13f68888, "CMI8888"),
92*0f1e8306STakashi Iwai HDA_CODEC_ID(0x13f69880, "CMI9880"),
93*0f1e8306STakashi Iwai HDA_CODEC_ID(0x434d4980, "CMI9880"),
946014e902STakashi Iwai {} /* terminator */
956014e902STakashi Iwai };
966014e902STakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
976014e902STakashi Iwai
986014e902STakashi Iwai MODULE_LICENSE("GPL");
996014e902STakashi Iwai MODULE_DESCRIPTION("C-Media HD-audio codec");
1006014e902STakashi Iwai
1016014e902STakashi Iwai static struct hda_codec_driver cmedia_driver = {
1026014e902STakashi Iwai .id = snd_hda_id_cmedia,
103*0f1e8306STakashi Iwai .ops = &cmedia_codec_ops,
1046014e902STakashi Iwai };
1056014e902STakashi Iwai
1066014e902STakashi Iwai module_hda_codec_driver(cmedia_driver);
107