xref: /openbmc/linux/sound/soc/codecs/hdac_hdmi.c (revision b0362adbeb95b57d9739b0744772eaf9feaa6e5e)
118382eadSSubhransu S. Prusty /*
218382eadSSubhransu S. Prusty  *  hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
318382eadSSubhransu S. Prusty  *
418382eadSSubhransu S. Prusty  *  Copyright (C) 2014-2015 Intel Corp
518382eadSSubhransu S. Prusty  *  Author: Samreen Nilofer <samreen.nilofer@intel.com>
618382eadSSubhransu S. Prusty  *	    Subhransu S. Prusty <subhransu.s.prusty@intel.com>
718382eadSSubhransu S. Prusty  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
818382eadSSubhransu S. Prusty  *
918382eadSSubhransu S. Prusty  *  This program is free software; you can redistribute it and/or modify
1018382eadSSubhransu S. Prusty  *  it under the terms of the GNU General Public License as published by
1118382eadSSubhransu S. Prusty  *  the Free Software Foundation; version 2 of the License.
1218382eadSSubhransu S. Prusty  *
1318382eadSSubhransu S. Prusty  *  This program is distributed in the hope that it will be useful, but
1418382eadSSubhransu S. Prusty  *  WITHOUT ANY WARRANTY; without even the implied warranty of
1518382eadSSubhransu S. Prusty  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1618382eadSSubhransu S. Prusty  *  General Public License for more details.
1718382eadSSubhransu S. Prusty  *
1818382eadSSubhransu S. Prusty  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1918382eadSSubhransu S. Prusty  */
2018382eadSSubhransu S. Prusty #include <linux/init.h>
2118382eadSSubhransu S. Prusty #include <linux/delay.h>
2218382eadSSubhransu S. Prusty #include <linux/module.h>
2318382eadSSubhransu S. Prusty #include <linux/pm_runtime.h>
2418382eadSSubhransu S. Prusty #include <sound/pcm_params.h>
2518382eadSSubhransu S. Prusty #include <sound/soc.h>
2618382eadSSubhransu S. Prusty #include <sound/hdaudio_ext.h>
2718382eadSSubhransu S. Prusty #include "../../hda/local.h"
2818382eadSSubhransu S. Prusty 
29*b0362adbSSubhransu S. Prusty #define AMP_OUT_MUTE		0xb080
30*b0362adbSSubhransu S. Prusty #define AMP_OUT_UNMUTE		0xb000
3118382eadSSubhransu S. Prusty #define PIN_OUT			(AC_PINCTL_OUT_EN)
32*b0362adbSSubhransu S. Prusty 
3318382eadSSubhransu S. Prusty #define HDA_MAX_CONNECTIONS     32
3418382eadSSubhransu S. Prusty 
3518382eadSSubhransu S. Prusty struct hdac_hdmi_cvt_params {
3618382eadSSubhransu S. Prusty 	unsigned int channels_min;
3718382eadSSubhransu S. Prusty 	unsigned int channels_max;
3818382eadSSubhransu S. Prusty 	u32 rates;
3918382eadSSubhransu S. Prusty 	u64 formats;
4018382eadSSubhransu S. Prusty 	unsigned int maxbps;
4118382eadSSubhransu S. Prusty };
4218382eadSSubhransu S. Prusty 
4318382eadSSubhransu S. Prusty struct hdac_hdmi_cvt {
4418382eadSSubhransu S. Prusty 	hda_nid_t nid;
4518382eadSSubhransu S. Prusty 	struct hdac_hdmi_cvt_params params;
4618382eadSSubhransu S. Prusty };
4718382eadSSubhransu S. Prusty 
4818382eadSSubhransu S. Prusty struct hdac_hdmi_pin {
4918382eadSSubhransu S. Prusty 	hda_nid_t nid;
5018382eadSSubhransu S. Prusty 	int num_mux_nids;
5118382eadSSubhransu S. Prusty 	hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
5218382eadSSubhransu S. Prusty };
5318382eadSSubhransu S. Prusty 
5418382eadSSubhransu S. Prusty struct hdac_hdmi_dai_pin_map {
5518382eadSSubhransu S. Prusty 	int dai_id;
5618382eadSSubhransu S. Prusty 	struct hdac_hdmi_pin pin;
5718382eadSSubhransu S. Prusty 	struct hdac_hdmi_cvt cvt;
5818382eadSSubhransu S. Prusty };
5918382eadSSubhransu S. Prusty 
6018382eadSSubhransu S. Prusty struct hdac_hdmi_priv {
6118382eadSSubhransu S. Prusty 	hda_nid_t pin_nid[3];
6218382eadSSubhransu S. Prusty 	hda_nid_t cvt_nid[3];
6318382eadSSubhransu S. Prusty 	struct hdac_hdmi_dai_pin_map dai_map[3];
6418382eadSSubhransu S. Prusty };
6518382eadSSubhransu S. Prusty 
66e342ac08SSubhransu S. Prusty static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
67e342ac08SSubhransu S. Prusty {
68e342ac08SSubhransu S. Prusty 	struct hdac_device *hdac = container_of(dev, struct hdac_device, dev);
69e342ac08SSubhransu S. Prusty 
70e342ac08SSubhransu S. Prusty 	return container_of(hdac, struct hdac_ext_device, hdac);
71e342ac08SSubhransu S. Prusty }
72e342ac08SSubhransu S. Prusty 
73*b0362adbSSubhransu S. Prusty static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
74*b0362adbSSubhransu S. Prusty 				hda_nid_t cvt_nid, hda_nid_t pin_nid,
75*b0362adbSSubhransu S. Prusty 				u32 stream_tag, int format)
76*b0362adbSSubhransu S. Prusty {
77*b0362adbSSubhransu S. Prusty 	unsigned int val;
78*b0362adbSSubhransu S. Prusty 
79*b0362adbSSubhransu S. Prusty 	dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
80*b0362adbSSubhransu S. Prusty 			cvt_nid, pin_nid, stream_tag, format);
81*b0362adbSSubhransu S. Prusty 
82*b0362adbSSubhransu S. Prusty 	val = (stream_tag << 4);
83*b0362adbSSubhransu S. Prusty 
84*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
85*b0362adbSSubhransu S. Prusty 				AC_VERB_SET_CHANNEL_STREAMID, val);
86*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
87*b0362adbSSubhransu S. Prusty 				AC_VERB_SET_STREAM_FORMAT, format);
88*b0362adbSSubhransu S. Prusty 
89*b0362adbSSubhransu S. Prusty 	return 0;
90*b0362adbSSubhransu S. Prusty }
91*b0362adbSSubhransu S. Prusty 
92*b0362adbSSubhransu S. Prusty static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
93*b0362adbSSubhransu S. Prusty 		struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
94*b0362adbSSubhransu S. Prusty {
95*b0362adbSSubhransu S. Prusty 	/* Power up pin widget */
96*b0362adbSSubhransu S. Prusty 	if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin.nid, pwr_state))
97*b0362adbSSubhransu S. Prusty 		snd_hdac_codec_write(&edev->hdac, dai_map->pin.nid, 0,
98*b0362adbSSubhransu S. Prusty 			AC_VERB_SET_POWER_STATE, pwr_state);
99*b0362adbSSubhransu S. Prusty 
100*b0362adbSSubhransu S. Prusty 	/* Power up converter */
101*b0362adbSSubhransu S. Prusty 	if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt.nid, pwr_state))
102*b0362adbSSubhransu S. Prusty 		snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
103*b0362adbSSubhransu S. Prusty 			AC_VERB_SET_POWER_STATE, pwr_state);
104*b0362adbSSubhransu S. Prusty }
105*b0362adbSSubhransu S. Prusty 
106*b0362adbSSubhransu S. Prusty static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
107*b0362adbSSubhransu S. Prusty 				struct snd_soc_dai *dai)
108*b0362adbSSubhransu S. Prusty {
109*b0362adbSSubhransu S. Prusty 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
110*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
111*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_dai_pin_map *dai_map;
112*b0362adbSSubhransu S. Prusty 	struct hdac_ext_dma_params *dd;
113*b0362adbSSubhransu S. Prusty 
114*b0362adbSSubhransu S. Prusty 	if (dai->id > 0) {
115*b0362adbSSubhransu S. Prusty 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
116*b0362adbSSubhransu S. Prusty 		return -ENODEV;
117*b0362adbSSubhransu S. Prusty 	}
118*b0362adbSSubhransu S. Prusty 
119*b0362adbSSubhransu S. Prusty 	dai_map = &hdmi->dai_map[dai->id];
120*b0362adbSSubhransu S. Prusty 
121*b0362adbSSubhransu S. Prusty 	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
122*b0362adbSSubhransu S. Prusty 	dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
123*b0362adbSSubhransu S. Prusty 			dd->stream_tag,	dd->format);
124*b0362adbSSubhransu S. Prusty 
125*b0362adbSSubhransu S. Prusty 	return hdac_hdmi_setup_stream(hdac, dai_map->cvt.nid, dai_map->pin.nid,
126*b0362adbSSubhransu S. Prusty 					dd->stream_tag, dd->format);
127*b0362adbSSubhransu S. Prusty }
128*b0362adbSSubhransu S. Prusty 
129*b0362adbSSubhransu S. Prusty static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
130*b0362adbSSubhransu S. Prusty 	struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
131*b0362adbSSubhransu S. Prusty {
132*b0362adbSSubhransu S. Prusty 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
133*b0362adbSSubhransu S. Prusty 	struct hdac_ext_dma_params *dd;
134*b0362adbSSubhransu S. Prusty 
135*b0362adbSSubhransu S. Prusty 	if (dai->id > 0) {
136*b0362adbSSubhransu S. Prusty 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
137*b0362adbSSubhransu S. Prusty 		return -ENODEV;
138*b0362adbSSubhransu S. Prusty 	}
139*b0362adbSSubhransu S. Prusty 
140*b0362adbSSubhransu S. Prusty 	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
141*b0362adbSSubhransu S. Prusty 	dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
142*b0362adbSSubhransu S. Prusty 			params_channels(hparams), params_format(hparams),
143*b0362adbSSubhransu S. Prusty 			24, 0);
144*b0362adbSSubhransu S. Prusty 
145*b0362adbSSubhransu S. Prusty 	snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
146*b0362adbSSubhransu S. Prusty 
147*b0362adbSSubhransu S. Prusty 	return 0;
148*b0362adbSSubhransu S. Prusty }
149*b0362adbSSubhransu S. Prusty 
150*b0362adbSSubhransu S. Prusty static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
151*b0362adbSSubhransu S. Prusty 		struct snd_soc_dai *dai)
152*b0362adbSSubhransu S. Prusty {
153*b0362adbSSubhransu S. Prusty 	struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
154*b0362adbSSubhransu S. Prusty 	struct hdac_ext_dma_params *dd;
155*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = edev->private_data;
156*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_dai_pin_map *dai_map;
157*b0362adbSSubhransu S. Prusty 
158*b0362adbSSubhransu S. Prusty 	dai_map = &hdmi->dai_map[dai->id];
159*b0362adbSSubhransu S. Prusty 
160*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
161*b0362adbSSubhransu S. Prusty 				AC_VERB_SET_CHANNEL_STREAMID, 0);
162*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
163*b0362adbSSubhransu S. Prusty 				AC_VERB_SET_STREAM_FORMAT, 0);
164*b0362adbSSubhransu S. Prusty 
165*b0362adbSSubhransu S. Prusty 	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
166*b0362adbSSubhransu S. Prusty 	snd_soc_dai_set_dma_data(dai, substream, NULL);
167*b0362adbSSubhransu S. Prusty 
168*b0362adbSSubhransu S. Prusty 	kfree(dd);
169*b0362adbSSubhransu S. Prusty 
170*b0362adbSSubhransu S. Prusty 	return 0;
171*b0362adbSSubhransu S. Prusty }
172*b0362adbSSubhransu S. Prusty 
173*b0362adbSSubhransu S. Prusty static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
174*b0362adbSSubhransu S. Prusty 			struct snd_soc_dai *dai)
175*b0362adbSSubhransu S. Prusty {
176*b0362adbSSubhransu S. Prusty 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
177*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
178*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_dai_pin_map *dai_map;
179*b0362adbSSubhransu S. Prusty 	int val;
180*b0362adbSSubhransu S. Prusty 
181*b0362adbSSubhransu S. Prusty 	if (dai->id > 0) {
182*b0362adbSSubhransu S. Prusty 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
183*b0362adbSSubhransu S. Prusty 		return -ENODEV;
184*b0362adbSSubhransu S. Prusty 	}
185*b0362adbSSubhransu S. Prusty 
186*b0362adbSSubhransu S. Prusty 	dai_map = &hdmi->dai_map[dai->id];
187*b0362adbSSubhransu S. Prusty 
188*b0362adbSSubhransu S. Prusty 	val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin.nid, 0,
189*b0362adbSSubhransu S. Prusty 					AC_VERB_GET_PIN_SENSE, 0);
190*b0362adbSSubhransu S. Prusty 	dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
191*b0362adbSSubhransu S. Prusty 
192*b0362adbSSubhransu S. Prusty 	if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
193*b0362adbSSubhransu S. Prusty 		dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
194*b0362adbSSubhransu S. Prusty 		return -ENODEV;
195*b0362adbSSubhransu S. Prusty 	}
196*b0362adbSSubhransu S. Prusty 
197*b0362adbSSubhransu S. Prusty 	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
198*b0362adbSSubhransu S. Prusty 
199*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
200*b0362adbSSubhransu S. Prusty 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
201*b0362adbSSubhransu S. Prusty 
202*b0362adbSSubhransu S. Prusty 	snd_pcm_hw_constraint_step(substream->runtime, 0,
203*b0362adbSSubhransu S. Prusty 				   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
204*b0362adbSSubhransu S. Prusty 
205*b0362adbSSubhransu S. Prusty 	return 0;
206*b0362adbSSubhransu S. Prusty }
207*b0362adbSSubhransu S. Prusty 
208*b0362adbSSubhransu S. Prusty static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
209*b0362adbSSubhransu S. Prusty 		struct snd_soc_dai *dai)
210*b0362adbSSubhransu S. Prusty {
211*b0362adbSSubhransu S. Prusty 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
212*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
213*b0362adbSSubhransu S. Prusty 	struct hdac_hdmi_dai_pin_map *dai_map;
214*b0362adbSSubhransu S. Prusty 
215*b0362adbSSubhransu S. Prusty 	dai_map = &hdmi->dai_map[dai->id];
216*b0362adbSSubhransu S. Prusty 
217*b0362adbSSubhransu S. Prusty 	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
218*b0362adbSSubhransu S. Prusty 
219*b0362adbSSubhransu S. Prusty 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
220*b0362adbSSubhransu S. Prusty 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
221*b0362adbSSubhransu S. Prusty }
222*b0362adbSSubhransu S. Prusty 
22318382eadSSubhransu S. Prusty static int
22418382eadSSubhransu S. Prusty hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
22518382eadSSubhransu S. Prusty {
22618382eadSSubhransu S. Prusty 	int err;
22718382eadSSubhransu S. Prusty 
22818382eadSSubhransu S. Prusty 	/* Only stereo supported as of now */
22918382eadSSubhransu S. Prusty 	cvt->params.channels_min = cvt->params.channels_max = 2;
23018382eadSSubhransu S. Prusty 
23118382eadSSubhransu S. Prusty 	err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
23218382eadSSubhransu S. Prusty 			&cvt->params.rates,
23318382eadSSubhransu S. Prusty 			&cvt->params.formats,
23418382eadSSubhransu S. Prusty 			&cvt->params.maxbps);
23518382eadSSubhransu S. Prusty 	if (err < 0)
23618382eadSSubhransu S. Prusty 		dev_err(&hdac->dev,
23718382eadSSubhransu S. Prusty 			"Failed to query pcm params for nid %d: %d\n",
23818382eadSSubhransu S. Prusty 			cvt->nid, err);
23918382eadSSubhransu S. Prusty 
24018382eadSSubhransu S. Prusty 	return err;
24118382eadSSubhransu S. Prusty }
24218382eadSSubhransu S. Prusty 
24318382eadSSubhransu S. Prusty static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
24418382eadSSubhransu S. Prusty 					struct hdac_hdmi_pin *pin)
24518382eadSSubhransu S. Prusty {
24618382eadSSubhransu S. Prusty 	if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
24718382eadSSubhransu S. Prusty 		dev_warn(&hdac->hdac.dev,
24818382eadSSubhransu S. Prusty 			"HDMI: pin %d wcaps %#x does not support connection list\n",
24918382eadSSubhransu S. Prusty 			pin->nid, get_wcaps(&hdac->hdac, pin->nid));
25018382eadSSubhransu S. Prusty 		return -EINVAL;
25118382eadSSubhransu S. Prusty 	}
25218382eadSSubhransu S. Prusty 
25318382eadSSubhransu S. Prusty 	pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
25418382eadSSubhransu S. Prusty 			pin->mux_nids, HDA_MAX_CONNECTIONS);
25518382eadSSubhransu S. Prusty 	if (pin->num_mux_nids == 0) {
25618382eadSSubhransu S. Prusty 		dev_err(&hdac->hdac.dev, "No connections found\n");
25718382eadSSubhransu S. Prusty 		return -ENODEV;
25818382eadSSubhransu S. Prusty 	}
25918382eadSSubhransu S. Prusty 
26018382eadSSubhransu S. Prusty 	return pin->num_mux_nids;
26118382eadSSubhransu S. Prusty }
26218382eadSSubhransu S. Prusty 
26318382eadSSubhransu S. Prusty static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
26418382eadSSubhransu S. Prusty 				enum snd_soc_dapm_type id,
26518382eadSSubhransu S. Prusty 				const char *wname, const char *stream)
26618382eadSSubhransu S. Prusty {
26718382eadSSubhransu S. Prusty 	w->id = id;
26818382eadSSubhransu S. Prusty 	w->name = wname;
26918382eadSSubhransu S. Prusty 	w->sname = stream;
27018382eadSSubhransu S. Prusty 	w->reg = SND_SOC_NOPM;
27118382eadSSubhransu S. Prusty 	w->shift = 0;
27218382eadSSubhransu S. Prusty 	w->kcontrol_news = NULL;
27318382eadSSubhransu S. Prusty 	w->num_kcontrols = 0;
27418382eadSSubhransu S. Prusty 	w->priv = NULL;
27518382eadSSubhransu S. Prusty }
27618382eadSSubhransu S. Prusty 
27718382eadSSubhransu S. Prusty static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
27818382eadSSubhransu S. Prusty 		const char *sink, const char *control, const char *src)
27918382eadSSubhransu S. Prusty {
28018382eadSSubhransu S. Prusty 	route->sink = sink;
28118382eadSSubhransu S. Prusty 	route->source = src;
28218382eadSSubhransu S. Prusty 	route->control = control;
28318382eadSSubhransu S. Prusty 	route->connected = NULL;
28418382eadSSubhransu S. Prusty }
28518382eadSSubhransu S. Prusty 
28618382eadSSubhransu S. Prusty static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
28718382eadSSubhransu S. Prusty 					struct hdac_hdmi_dai_pin_map *dai_map)
28818382eadSSubhransu S. Prusty {
28918382eadSSubhransu S. Prusty 	struct snd_soc_dapm_route route[1];
29018382eadSSubhransu S. Prusty 	struct snd_soc_dapm_widget widgets[2] = { {0} };
29118382eadSSubhransu S. Prusty 
29218382eadSSubhransu S. Prusty 	memset(&route, 0, sizeof(route));
29318382eadSSubhransu S. Prusty 
29418382eadSSubhransu S. Prusty 	hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
29518382eadSSubhransu S. Prusty 			"hif1 Output", NULL);
29618382eadSSubhransu S. Prusty 	hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
29718382eadSSubhransu S. Prusty 			"Coverter 1", "hif1");
29818382eadSSubhransu S. Prusty 
29918382eadSSubhransu S. Prusty 	hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
30018382eadSSubhransu S. Prusty 
30118382eadSSubhransu S. Prusty 	snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
30218382eadSSubhransu S. Prusty 	snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
30318382eadSSubhransu S. Prusty }
30418382eadSSubhransu S. Prusty 
30518382eadSSubhransu S. Prusty static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev,
30618382eadSSubhransu S. Prusty 			struct hdac_hdmi_dai_pin_map *dai_map,
30718382eadSSubhransu S. Prusty 			hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id)
30818382eadSSubhransu S. Prusty {
30918382eadSSubhransu S. Prusty 	int ret;
31018382eadSSubhransu S. Prusty 
31118382eadSSubhransu S. Prusty 	dai_map->dai_id = dai_id;
31218382eadSSubhransu S. Prusty 	dai_map->pin.nid = pin_nid;
31318382eadSSubhransu S. Prusty 
31418382eadSSubhransu S. Prusty 	ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin);
31518382eadSSubhransu S. Prusty 	if (ret < 0) {
31618382eadSSubhransu S. Prusty 		dev_err(&edev->hdac.dev,
31718382eadSSubhransu S. Prusty 			"Error querying connection list: %d\n", ret);
31818382eadSSubhransu S. Prusty 		return ret;
31918382eadSSubhransu S. Prusty 	}
32018382eadSSubhransu S. Prusty 
32118382eadSSubhransu S. Prusty 	dai_map->cvt.nid = cvt_nid;
32218382eadSSubhransu S. Prusty 
32318382eadSSubhransu S. Prusty 	/* Enable out path for this pin widget */
32418382eadSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
32518382eadSSubhransu S. Prusty 			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
32618382eadSSubhransu S. Prusty 
32718382eadSSubhransu S. Prusty 	/* Enable transmission */
32818382eadSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
32918382eadSSubhransu S. Prusty 			AC_VERB_SET_DIGI_CONVERT_1, 1);
33018382eadSSubhransu S. Prusty 
33118382eadSSubhransu S. Prusty 	/* Category Code (CC) to zero */
33218382eadSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
33318382eadSSubhransu S. Prusty 			AC_VERB_SET_DIGI_CONVERT_2, 0);
33418382eadSSubhransu S. Prusty 
33518382eadSSubhransu S. Prusty 	snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
33618382eadSSubhransu S. Prusty 			AC_VERB_SET_CONNECT_SEL, 0);
33718382eadSSubhransu S. Prusty 
33818382eadSSubhransu S. Prusty 	return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt);
33918382eadSSubhransu S. Prusty }
34018382eadSSubhransu S. Prusty 
34118382eadSSubhransu S. Prusty /*
34218382eadSSubhransu S. Prusty  * Parse all nodes and store the cvt/pin nids in array
34318382eadSSubhransu S. Prusty  * Add one time initialization for pin and cvt widgets
34418382eadSSubhransu S. Prusty  */
34518382eadSSubhransu S. Prusty static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
34618382eadSSubhransu S. Prusty {
34718382eadSSubhransu S. Prusty 	hda_nid_t nid;
34818382eadSSubhransu S. Prusty 	int i;
34918382eadSSubhransu S. Prusty 	struct hdac_device *hdac = &edev->hdac;
35018382eadSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = edev->private_data;
35118382eadSSubhransu S. Prusty 	int cvt_nid = 0, pin_nid = 0;
35218382eadSSubhransu S. Prusty 
35318382eadSSubhransu S. Prusty 	hdac->num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
35418382eadSSubhransu S. Prusty 	if (!nid || hdac->num_nodes < 0) {
35518382eadSSubhransu S. Prusty 		dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
35618382eadSSubhransu S. Prusty 		return -EINVAL;
35718382eadSSubhransu S. Prusty 	}
35818382eadSSubhransu S. Prusty 
35918382eadSSubhransu S. Prusty 	hdac->start_nid = nid;
36018382eadSSubhransu S. Prusty 
36118382eadSSubhransu S. Prusty 	for (i = 0; i < hdac->num_nodes; i++, nid++) {
36218382eadSSubhransu S. Prusty 		unsigned int caps;
36318382eadSSubhransu S. Prusty 		unsigned int type;
36418382eadSSubhransu S. Prusty 
36518382eadSSubhransu S. Prusty 		caps = get_wcaps(hdac, nid);
36618382eadSSubhransu S. Prusty 		type = get_wcaps_type(caps);
36718382eadSSubhransu S. Prusty 
36818382eadSSubhransu S. Prusty 		if (!(caps & AC_WCAP_DIGITAL))
36918382eadSSubhransu S. Prusty 			continue;
37018382eadSSubhransu S. Prusty 
37118382eadSSubhransu S. Prusty 		switch (type) {
37218382eadSSubhransu S. Prusty 
37318382eadSSubhransu S. Prusty 		case AC_WID_AUD_OUT:
37418382eadSSubhransu S. Prusty 			hdmi->cvt_nid[cvt_nid] = nid;
37518382eadSSubhransu S. Prusty 			cvt_nid++;
37618382eadSSubhransu S. Prusty 			break;
37718382eadSSubhransu S. Prusty 
37818382eadSSubhransu S. Prusty 		case AC_WID_PIN:
37918382eadSSubhransu S. Prusty 			hdmi->pin_nid[pin_nid] = nid;
38018382eadSSubhransu S. Prusty 			pin_nid++;
38118382eadSSubhransu S. Prusty 			break;
38218382eadSSubhransu S. Prusty 		}
38318382eadSSubhransu S. Prusty 	}
38418382eadSSubhransu S. Prusty 
38518382eadSSubhransu S. Prusty 	hdac->end_nid = nid;
38618382eadSSubhransu S. Prusty 
38718382eadSSubhransu S. Prusty 	if (!pin_nid || !cvt_nid)
38818382eadSSubhransu S. Prusty 		return -EIO;
38918382eadSSubhransu S. Prusty 
39018382eadSSubhransu S. Prusty 	/*
39118382eadSSubhransu S. Prusty 	 * Currently on board only 1 pin and 1 converter is enabled for
39218382eadSSubhransu S. Prusty 	 * simplification, more will be added eventually
39318382eadSSubhransu S. Prusty 	 * So using fixed map for dai_id:pin:cvt
39418382eadSSubhransu S. Prusty 	 */
39518382eadSSubhransu S. Prusty 	return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0],
39618382eadSSubhransu S. Prusty 			hdmi->cvt_nid[0], 0);
39718382eadSSubhransu S. Prusty }
39818382eadSSubhransu S. Prusty 
39918382eadSSubhransu S. Prusty static int hdmi_codec_probe(struct snd_soc_codec *codec)
40018382eadSSubhransu S. Prusty {
40118382eadSSubhransu S. Prusty 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
40218382eadSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi = edev->private_data;
40318382eadSSubhransu S. Prusty 	struct snd_soc_dapm_context *dapm =
40418382eadSSubhransu S. Prusty 		snd_soc_component_get_dapm(&codec->component);
40518382eadSSubhransu S. Prusty 
40618382eadSSubhransu S. Prusty 	edev->scodec = codec;
40718382eadSSubhransu S. Prusty 
40818382eadSSubhransu S. Prusty 	create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
40918382eadSSubhransu S. Prusty 
41018382eadSSubhransu S. Prusty 	/* Imp: Store the card pointer in hda_codec */
41118382eadSSubhransu S. Prusty 	edev->card = dapm->card->snd_card;
41218382eadSSubhransu S. Prusty 
413e342ac08SSubhransu S. Prusty 	/*
414e342ac08SSubhransu S. Prusty 	 * hdac_device core already sets the state to active and calls
415e342ac08SSubhransu S. Prusty 	 * get_noresume. So enable runtime and set the device to suspend.
416e342ac08SSubhransu S. Prusty 	 */
417e342ac08SSubhransu S. Prusty 	pm_runtime_enable(&edev->hdac.dev);
418e342ac08SSubhransu S. Prusty 	pm_runtime_put(&edev->hdac.dev);
419e342ac08SSubhransu S. Prusty 	pm_runtime_suspend(&edev->hdac.dev);
420e342ac08SSubhransu S. Prusty 
421e342ac08SSubhransu S. Prusty 	return 0;
422e342ac08SSubhransu S. Prusty }
423e342ac08SSubhransu S. Prusty 
424e342ac08SSubhransu S. Prusty static int hdmi_codec_remove(struct snd_soc_codec *codec)
425e342ac08SSubhransu S. Prusty {
426e342ac08SSubhransu S. Prusty 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
427e342ac08SSubhransu S. Prusty 
428e342ac08SSubhransu S. Prusty 	pm_runtime_disable(&edev->hdac.dev);
42918382eadSSubhransu S. Prusty 	return 0;
43018382eadSSubhransu S. Prusty }
43118382eadSSubhransu S. Prusty 
43218382eadSSubhransu S. Prusty static struct snd_soc_codec_driver hdmi_hda_codec = {
43318382eadSSubhransu S. Prusty 	.probe		= hdmi_codec_probe,
434e342ac08SSubhransu S. Prusty 	.remove		= hdmi_codec_remove,
43518382eadSSubhransu S. Prusty 	.idle_bias_off	= true,
43618382eadSSubhransu S. Prusty };
43718382eadSSubhransu S. Prusty 
438*b0362adbSSubhransu S. Prusty static struct snd_soc_dai_ops hdmi_dai_ops = {
439*b0362adbSSubhransu S. Prusty 	.startup = hdac_hdmi_pcm_open,
440*b0362adbSSubhransu S. Prusty 	.shutdown = hdac_hdmi_pcm_close,
441*b0362adbSSubhransu S. Prusty 	.hw_params = hdac_hdmi_set_hw_params,
442*b0362adbSSubhransu S. Prusty 	.prepare = hdac_hdmi_playback_prepare,
443*b0362adbSSubhransu S. Prusty 	.hw_free = hdac_hdmi_playback_cleanup,
444*b0362adbSSubhransu S. Prusty };
445*b0362adbSSubhransu S. Prusty 
44618382eadSSubhransu S. Prusty static struct snd_soc_dai_driver hdmi_dais[] = {
44718382eadSSubhransu S. Prusty 	{	.name = "intel-hdmi-hif1",
44818382eadSSubhransu S. Prusty 		.playback = {
44918382eadSSubhransu S. Prusty 			.stream_name = "hif1",
45018382eadSSubhransu S. Prusty 			.channels_min = 2,
45118382eadSSubhransu S. Prusty 			.channels_max = 2,
45218382eadSSubhransu S. Prusty 			.rates = SNDRV_PCM_RATE_32000 |
45318382eadSSubhransu S. Prusty 				SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
45418382eadSSubhransu S. Prusty 				SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
45518382eadSSubhransu S. Prusty 				SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
45618382eadSSubhransu S. Prusty 			.formats = SNDRV_PCM_FMTBIT_S16_LE |
45718382eadSSubhransu S. Prusty 				SNDRV_PCM_FMTBIT_S20_3LE |
45818382eadSSubhransu S. Prusty 				SNDRV_PCM_FMTBIT_S24_LE |
45918382eadSSubhransu S. Prusty 				SNDRV_PCM_FMTBIT_S32_LE,
46018382eadSSubhransu S. Prusty 
46118382eadSSubhransu S. Prusty 		},
462*b0362adbSSubhransu S. Prusty 		.ops = &hdmi_dai_ops,
46318382eadSSubhransu S. Prusty 	},
46418382eadSSubhransu S. Prusty };
46518382eadSSubhransu S. Prusty 
46618382eadSSubhransu S. Prusty static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
46718382eadSSubhransu S. Prusty {
46818382eadSSubhransu S. Prusty 	struct hdac_device *codec = &edev->hdac;
46918382eadSSubhransu S. Prusty 	struct hdac_hdmi_priv *hdmi_priv;
47018382eadSSubhransu S. Prusty 	int ret = 0;
47118382eadSSubhransu S. Prusty 
47218382eadSSubhransu S. Prusty 	hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
47318382eadSSubhransu S. Prusty 	if (hdmi_priv == NULL)
47418382eadSSubhransu S. Prusty 		return -ENOMEM;
47518382eadSSubhransu S. Prusty 
47618382eadSSubhransu S. Prusty 	edev->private_data = hdmi_priv;
47718382eadSSubhransu S. Prusty 
47818382eadSSubhransu S. Prusty 	dev_set_drvdata(&codec->dev, edev);
47918382eadSSubhransu S. Prusty 
48018382eadSSubhransu S. Prusty 	ret = hdac_hdmi_parse_and_map_nid(edev);
48118382eadSSubhransu S. Prusty 	if (ret < 0)
48218382eadSSubhransu S. Prusty 		return ret;
48318382eadSSubhransu S. Prusty 
48418382eadSSubhransu S. Prusty 	/* ASoC specific initialization */
48518382eadSSubhransu S. Prusty 	return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
48618382eadSSubhransu S. Prusty 			hdmi_dais, ARRAY_SIZE(hdmi_dais));
48718382eadSSubhransu S. Prusty }
48818382eadSSubhransu S. Prusty 
48918382eadSSubhransu S. Prusty static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
49018382eadSSubhransu S. Prusty {
49118382eadSSubhransu S. Prusty 	snd_soc_unregister_codec(&edev->hdac.dev);
49218382eadSSubhransu S. Prusty 
49318382eadSSubhransu S. Prusty 	return 0;
49418382eadSSubhransu S. Prusty }
49518382eadSSubhransu S. Prusty 
496e342ac08SSubhransu S. Prusty #ifdef CONFIG_PM
497e342ac08SSubhransu S. Prusty static int hdac_hdmi_runtime_suspend(struct device *dev)
498e342ac08SSubhransu S. Prusty {
499e342ac08SSubhransu S. Prusty 	struct hdac_ext_device *edev = to_hda_ext_device(dev);
500e342ac08SSubhransu S. Prusty 	struct hdac_device *hdac = &edev->hdac;
501e342ac08SSubhransu S. Prusty 
502e342ac08SSubhransu S. Prusty 	dev_dbg(dev, "Enter: %s\n", __func__);
503e342ac08SSubhransu S. Prusty 
504e342ac08SSubhransu S. Prusty 	/* Power down afg */
505e342ac08SSubhransu S. Prusty 	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
506e342ac08SSubhransu S. Prusty 		snd_hdac_codec_write(hdac, hdac->afg, 0,
507e342ac08SSubhransu S. Prusty 			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
508e342ac08SSubhransu S. Prusty 
509e342ac08SSubhransu S. Prusty 	return 0;
510e342ac08SSubhransu S. Prusty }
511e342ac08SSubhransu S. Prusty 
512e342ac08SSubhransu S. Prusty static int hdac_hdmi_runtime_resume(struct device *dev)
513e342ac08SSubhransu S. Prusty {
514e342ac08SSubhransu S. Prusty 	struct hdac_ext_device *edev = to_hda_ext_device(dev);
515e342ac08SSubhransu S. Prusty 	struct hdac_device *hdac = &edev->hdac;
516e342ac08SSubhransu S. Prusty 
517e342ac08SSubhransu S. Prusty 	dev_dbg(dev, "Enter: %s\n", __func__);
518e342ac08SSubhransu S. Prusty 
519e342ac08SSubhransu S. Prusty 	/* Power up afg */
520e342ac08SSubhransu S. Prusty 	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
521e342ac08SSubhransu S. Prusty 		snd_hdac_codec_write(hdac, hdac->afg, 0,
522e342ac08SSubhransu S. Prusty 			AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
523e342ac08SSubhransu S. Prusty 
524e342ac08SSubhransu S. Prusty 	return 0;
525e342ac08SSubhransu S. Prusty }
526e342ac08SSubhransu S. Prusty #else
527e342ac08SSubhransu S. Prusty #define hdac_hdmi_runtime_suspend NULL
528e342ac08SSubhransu S. Prusty #define hdac_hdmi_runtime_resume NULL
529e342ac08SSubhransu S. Prusty #endif
530e342ac08SSubhransu S. Prusty 
531e342ac08SSubhransu S. Prusty static const struct dev_pm_ops hdac_hdmi_pm = {
532e342ac08SSubhransu S. Prusty 	SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
533e342ac08SSubhransu S. Prusty };
534e342ac08SSubhransu S. Prusty 
53518382eadSSubhransu S. Prusty static const struct hda_device_id hdmi_list[] = {
53618382eadSSubhransu S. Prusty 	HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
53718382eadSSubhransu S. Prusty 	{}
53818382eadSSubhransu S. Prusty };
53918382eadSSubhransu S. Prusty 
54018382eadSSubhransu S. Prusty MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
54118382eadSSubhransu S. Prusty 
54218382eadSSubhransu S. Prusty static struct hdac_ext_driver hdmi_driver = {
54318382eadSSubhransu S. Prusty 	. hdac = {
54418382eadSSubhransu S. Prusty 		.driver = {
54518382eadSSubhransu S. Prusty 			.name   = "HDMI HDA Codec",
546e342ac08SSubhransu S. Prusty 			.pm = &hdac_hdmi_pm,
54718382eadSSubhransu S. Prusty 		},
54818382eadSSubhransu S. Prusty 		.id_table       = hdmi_list,
54918382eadSSubhransu S. Prusty 	},
55018382eadSSubhransu S. Prusty 	.probe          = hdac_hdmi_dev_probe,
55118382eadSSubhransu S. Prusty 	.remove         = hdac_hdmi_dev_remove,
55218382eadSSubhransu S. Prusty };
55318382eadSSubhransu S. Prusty 
55418382eadSSubhransu S. Prusty static int __init hdmi_init(void)
55518382eadSSubhransu S. Prusty {
55618382eadSSubhransu S. Prusty 	return snd_hda_ext_driver_register(&hdmi_driver);
55718382eadSSubhransu S. Prusty }
55818382eadSSubhransu S. Prusty 
55918382eadSSubhransu S. Prusty static void __exit hdmi_exit(void)
56018382eadSSubhransu S. Prusty {
56118382eadSSubhransu S. Prusty 	snd_hda_ext_driver_unregister(&hdmi_driver);
56218382eadSSubhransu S. Prusty }
56318382eadSSubhransu S. Prusty 
56418382eadSSubhransu S. Prusty module_init(hdmi_init);
56518382eadSSubhransu S. Prusty module_exit(hdmi_exit);
56618382eadSSubhransu S. Prusty 
56718382eadSSubhransu S. Prusty MODULE_LICENSE("GPL v2");
56818382eadSSubhransu S. Prusty MODULE_DESCRIPTION("HDMI HD codec");
56918382eadSSubhransu S. Prusty MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
57018382eadSSubhransu S. Prusty MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
571