blob: dcd1cb64314583d0598d071dfd7b39e80f3d8a7f [file] [log] [blame]
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301/*
2 * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
3 *
4 * Copyright (C) 2014-2015 Intel Corp
5 * Author: Samreen Nilofer <samreen.nilofer@intel.com>
6 * Subhransu S. Prusty <subhransu.s.prusty@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 */
20#include <linux/init.h>
21#include <linux/delay.h>
22#include <linux/module.h>
23#include <linux/pm_runtime.h>
Subhransu S. Prustya657f1d2015-11-10 18:42:09 +053024#include <linux/hdmi.h>
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +053025#include <drm/drm_edid.h>
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053026#include <sound/pcm_params.h>
Jeeja KP4a3478d2016-02-12 07:46:06 +053027#include <sound/jack.h>
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053028#include <sound/soc.h>
29#include <sound/hdaudio_ext.h>
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +053030#include <sound/hda_i915.h>
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +053031#include <sound/pcm_drm_eld.h>
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053032#include "../../hda/local.h"
Jeeja KP4a3478d2016-02-12 07:46:06 +053033#include "hdac_hdmi.h"
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053034
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +053035#define NAME_SIZE 32
36
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +053037#define AMP_OUT_MUTE 0xb080
38#define AMP_OUT_UNMUTE 0xb000
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053039#define PIN_OUT (AC_PINCTL_OUT_EN)
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +053040
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053041#define HDA_MAX_CONNECTIONS 32
42
Subhransu S. Prusty148569f2016-02-12 07:46:07 +053043#define HDA_MAX_CVTS 3
44
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +053045#define ELD_MAX_SIZE 256
46#define ELD_FIXED_BYTES 20
47
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053048struct hdac_hdmi_cvt_params {
49 unsigned int channels_min;
50 unsigned int channels_max;
51 u32 rates;
52 u64 formats;
53 unsigned int maxbps;
54};
55
56struct hdac_hdmi_cvt {
Subhransu S. Prusty15b91442015-12-09 21:46:10 +053057 struct list_head head;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053058 hda_nid_t nid;
Jeeja KP4a3478d2016-02-12 07:46:06 +053059 const char *name;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053060 struct hdac_hdmi_cvt_params params;
61};
62
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +053063struct hdac_hdmi_eld {
64 bool monitor_present;
65 bool eld_valid;
66 int eld_size;
67 char eld_buffer[ELD_MAX_SIZE];
68};
69
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053070struct hdac_hdmi_pin {
Subhransu S. Prusty15b91442015-12-09 21:46:10 +053071 struct list_head head;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053072 hda_nid_t nid;
73 int num_mux_nids;
74 hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +053075 struct hdac_hdmi_eld eld;
76 struct hdac_ext_device *edev;
77 int repoll_count;
78 struct delayed_work work;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053079};
80
Jeeja KP4a3478d2016-02-12 07:46:06 +053081struct hdac_hdmi_pcm {
82 struct list_head head;
83 int pcm_id;
84 struct hdac_hdmi_pin *pin;
85 struct hdac_hdmi_cvt *cvt;
86 struct snd_jack *jack;
87};
88
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053089struct hdac_hdmi_dai_pin_map {
90 int dai_id;
Subhransu S. Prusty15b91442015-12-09 21:46:10 +053091 struct hdac_hdmi_pin *pin;
92 struct hdac_hdmi_cvt *cvt;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053093};
94
95struct hdac_hdmi_priv {
Subhransu S. Prusty148569f2016-02-12 07:46:07 +053096 struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
Subhransu S. Prusty15b91442015-12-09 21:46:10 +053097 struct list_head pin_list;
98 struct list_head cvt_list;
Jeeja KP4a3478d2016-02-12 07:46:06 +053099 struct list_head pcm_list;
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530100 int num_pin;
101 int num_cvt;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530102 struct mutex pin_mutex;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530103};
104
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530105static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
106{
Geliang Tang51b2c422015-12-28 22:47:13 +0800107 struct hdac_device *hdac = dev_to_hdac_dev(dev);
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530108
Geliang Tang51b2c422015-12-28 22:47:13 +0800109 return to_ehdac_device(hdac);
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530110}
111
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +0530112static unsigned int sad_format(const u8 *sad)
113{
114 return ((sad[0] >> 0x3) & 0x1f);
115}
116
117static unsigned int sad_sample_bits_lpcm(const u8 *sad)
118{
119 return (sad[2] & 7);
120}
121
122static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
123 void *eld)
124{
125 u64 formats = SNDRV_PCM_FMTBIT_S16;
126 int i;
127 const u8 *sad, *eld_buf = eld;
128
129 sad = drm_eld_sad(eld_buf);
130 if (!sad)
131 goto format_constraint;
132
133 for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
134 if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
135
136 /*
137 * the controller support 20 and 24 bits in 32 bit
138 * container so we set S32
139 */
140 if (sad_sample_bits_lpcm(sad) & 0x6)
141 formats |= SNDRV_PCM_FMTBIT_S32;
142 }
143 }
144
145format_constraint:
146 return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
147 formats);
148
149}
150
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530151 /* HDMI ELD routines */
152static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
153 hda_nid_t nid, int byte_index)
154{
155 unsigned int val;
156
157 val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
158 byte_index);
159
160 dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
161 byte_index, val);
162
163 return val;
164}
165
166static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
167{
168 return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
169 AC_DIPSIZE_ELD_BUF);
170}
171
172/*
173 * This function queries the ELD size and ELD data and fills in the buffer
174 * passed by user
175 */
176static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
177 unsigned char *buf, int *eld_size)
178{
179 int i, size, ret = 0;
180
181 /*
182 * ELD size is initialized to zero in caller function. If no errors and
183 * ELD is valid, actual eld_size is assigned.
184 */
185
186 size = hdac_hdmi_get_eld_size(codec, nid);
187 if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
188 dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
189 return -ERANGE;
190 }
191
192 /* set ELD buffer */
193 for (i = 0; i < size; i++) {
194 unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
195 /*
196 * Graphics driver might be writing to ELD buffer right now.
197 * Just abort. The caller will repoll after a while.
198 */
199 if (!(val & AC_ELDD_ELD_VALID)) {
200 dev_err(&codec->dev,
201 "HDMI: invalid ELD data byte %d\n", i);
202 ret = -EINVAL;
203 goto error;
204 }
205 val &= AC_ELDD_ELD_DATA;
206 /*
207 * The first byte cannot be zero. This can happen on some DVI
208 * connections. Some Intel chips may also need some 250ms delay
209 * to return non-zero ELD data, even when the graphics driver
210 * correctly writes ELD content before setting ELD_valid bit.
211 */
212 if (!val && !i) {
213 dev_err(&codec->dev, "HDMI: 0 ELD data\n");
214 ret = -EINVAL;
215 goto error;
216 }
217 buf[i] = val;
218 }
219
220 *eld_size = size;
221error:
222 return ret;
223}
224
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530225static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
226 hda_nid_t cvt_nid, hda_nid_t pin_nid,
227 u32 stream_tag, int format)
228{
229 unsigned int val;
230
231 dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
232 cvt_nid, pin_nid, stream_tag, format);
233
234 val = (stream_tag << 4);
235
236 snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
237 AC_VERB_SET_CHANNEL_STREAMID, val);
238 snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
239 AC_VERB_SET_STREAM_FORMAT, format);
240
241 return 0;
242}
243
Subhransu S. Prustya657f1d2015-11-10 18:42:09 +0530244static void
245hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
246 int packet_index, int byte_index)
247{
248 int val;
249
250 val = (packet_index << 5) | (byte_index & 0x1f);
251
252 snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
253 AC_VERB_SET_HDMI_DIP_INDEX, val);
254}
255
256static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
257 hda_nid_t cvt_nid, hda_nid_t pin_nid)
258{
259 uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
260 struct hdmi_audio_infoframe frame;
261 u8 *dip = (u8 *)&frame;
262 int ret;
263 int i;
264
265 hdmi_audio_infoframe_init(&frame);
266
267 /* Default stereo for now */
268 frame.channels = 2;
269
270 /* setup channel count */
271 snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
272 AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
273
274 ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
275 if (ret < 0)
276 return ret;
277
278 /* stop infoframe transmission */
279 hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
280 snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
281 AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
282
283
284 /* Fill infoframe. Index auto-incremented */
285 hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
286 for (i = 0; i < sizeof(frame); i++)
287 snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
288 AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
289
290 /* Start infoframe */
291 hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
292 snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
293 AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
294
295 return 0;
296}
297
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530298static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
299 struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
300{
301 /* Power up pin widget */
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530302 if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
303 pwr_state))
304 snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530305 AC_VERB_SET_POWER_STATE, pwr_state);
306
307 /* Power up converter */
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530308 if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
309 pwr_state))
310 snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530311 AC_VERB_SET_POWER_STATE, pwr_state);
312}
313
314static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
315 struct snd_soc_dai *dai)
316{
317 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
318 struct hdac_hdmi_priv *hdmi = hdac->private_data;
319 struct hdac_hdmi_dai_pin_map *dai_map;
320 struct hdac_ext_dma_params *dd;
Subhransu S. Prustya657f1d2015-11-10 18:42:09 +0530321 int ret;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530322
323 if (dai->id > 0) {
324 dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
325 return -ENODEV;
326 }
327
328 dai_map = &hdmi->dai_map[dai->id];
329
330 dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
331 dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
332 dd->stream_tag, dd->format);
333
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530334 ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
335 dai_map->pin->nid);
Subhransu S. Prustya657f1d2015-11-10 18:42:09 +0530336 if (ret < 0)
337 return ret;
338
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530339 return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
340 dai_map->pin->nid, dd->stream_tag, dd->format);
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530341}
342
343static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
344 struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
345{
346 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
347 struct hdac_ext_dma_params *dd;
348
349 if (dai->id > 0) {
350 dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
351 return -ENODEV;
352 }
353
354 dd = kzalloc(sizeof(*dd), GFP_KERNEL);
Sudip Mukherjee8d33ab22015-11-23 17:45:13 +0530355 if (!dd)
356 return -ENOMEM;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530357 dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
358 params_channels(hparams), params_format(hparams),
359 24, 0);
360
361 snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
362
363 return 0;
364}
365
366static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
367 struct snd_soc_dai *dai)
368{
369 struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
370 struct hdac_ext_dma_params *dd;
371 struct hdac_hdmi_priv *hdmi = edev->private_data;
372 struct hdac_hdmi_dai_pin_map *dai_map;
373
374 dai_map = &hdmi->dai_map[dai->id];
375
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530376 snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530377 AC_VERB_SET_CHANNEL_STREAMID, 0);
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530378 snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530379 AC_VERB_SET_STREAM_FORMAT, 0);
380
381 dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
382 snd_soc_dai_set_dma_data(dai, substream, NULL);
383
384 kfree(dd);
385
386 return 0;
387}
388
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530389static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
390 struct hdac_hdmi_dai_pin_map *dai_map)
391{
392 int mux_idx;
393 struct hdac_hdmi_pin *pin = dai_map->pin;
394
395 for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
396 if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
397 snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
398 AC_VERB_SET_CONNECT_SEL, mux_idx);
399 break;
400 }
401 }
402
403 if (mux_idx == pin->num_mux_nids)
404 return -EIO;
405
406 /* Enable out path for this pin widget */
407 snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
408 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
409
410 hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
411
412 snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
413 AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
414
415 return 0;
416}
417
418static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
419 struct hdac_hdmi_pin *pin)
420{
421 if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
422 dev_warn(&hdac->hdac.dev,
423 "HDMI: pin %d wcaps %#x does not support connection list\n",
424 pin->nid, get_wcaps(&hdac->hdac, pin->nid));
425 return -EINVAL;
426 }
427
428 pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
429 pin->mux_nids, HDA_MAX_CONNECTIONS);
430 if (pin->num_mux_nids == 0)
431 dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
432 pin->nid);
433
434 dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
435 pin->num_mux_nids, pin->nid);
436
437 return pin->num_mux_nids;
438}
439
440/*
441 * Query pcm list and return pin widget to which stream is routed.
442 *
443 * Also query connection list of the pin, to validate the cvt to pin map.
444 *
445 * Same stream rendering to multiple pins simultaneously can be done
446 * possibly, but not supported for now in driver. So return the first pin
447 * connected.
448 */
449static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
450 struct hdac_ext_device *edev,
451 struct hdac_hdmi_priv *hdmi,
452 struct hdac_hdmi_cvt *cvt)
453{
454 struct hdac_hdmi_pcm *pcm;
455 struct hdac_hdmi_pin *pin = NULL;
456 int ret, i;
457
458 list_for_each_entry(pcm, &hdmi->pcm_list, head) {
459 if (pcm->cvt == cvt) {
460 pin = pcm->pin;
461 break;
462 }
463 }
464
465 if (pin) {
466 ret = hdac_hdmi_query_pin_connlist(edev, pin);
467 if (ret < 0)
468 return NULL;
469
470 for (i = 0; i < pin->num_mux_nids; i++) {
471 if (pin->mux_nids[i] == cvt->nid)
472 return pin;
473 }
474 }
475
476 return NULL;
477}
478
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530479static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
480 struct snd_soc_dai *dai)
481{
482 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
483 struct hdac_hdmi_priv *hdmi = hdac->private_data;
484 struct hdac_hdmi_dai_pin_map *dai_map;
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530485 struct hdac_hdmi_cvt *cvt;
486 struct hdac_hdmi_pin *pin;
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +0530487 int ret;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530488
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530489 dai_map = &hdmi->dai_map[dai->id];
490
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530491 cvt = dai_map->cvt;
492 pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
493 if (!pin)
494 return -EIO;
495
496 if ((!pin->eld.monitor_present) ||
497 (!pin->eld.eld_valid)) {
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530498
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530499 dev_err(&hdac->hdac.dev,
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530500 "Failed: montior present? %d ELD valid?: %d for pin: %d\n",
501 pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530502
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530503 return -ENODEV;
504 }
505
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530506 dai_map->pin = pin;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530507
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530508 ret = hdac_hdmi_enable_pin(hdac, dai_map);
509 if (ret < 0)
510 return ret;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530511
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +0530512 ret = hdac_hdmi_eld_limit_formats(substream->runtime,
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530513 pin->eld.eld_buffer);
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +0530514 if (ret < 0)
515 return ret;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530516
Subhransu S. Prusty2428bca2016-02-12 07:46:02 +0530517 return snd_pcm_hw_constraint_eld(substream->runtime,
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530518 pin->eld.eld_buffer);
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530519}
520
521static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
522 struct snd_soc_dai *dai)
523{
524 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
525 struct hdac_hdmi_priv *hdmi = hdac->private_data;
526 struct hdac_hdmi_dai_pin_map *dai_map;
527
528 dai_map = &hdmi->dai_map[dai->id];
529
530 hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
531
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530532 snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530533 AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530534
535 dai_map->pin = NULL;
Subhransu S. Prustyb0362ad2015-11-10 18:42:08 +0530536}
537
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530538static int
539hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
540{
541 int err;
542
543 /* Only stereo supported as of now */
544 cvt->params.channels_min = cvt->params.channels_max = 2;
545
546 err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
547 &cvt->params.rates,
548 &cvt->params.formats,
549 &cvt->params.maxbps);
550 if (err < 0)
551 dev_err(&hdac->dev,
552 "Failed to query pcm params for nid %d: %d\n",
553 cvt->nid, err);
554
555 return err;
556}
557
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530558static int hdac_hdmi_fill_widget_info(struct device *dev,
559 struct snd_soc_dapm_widget *w,
560 enum snd_soc_dapm_type id, void *priv,
561 const char *wname, const char *stream,
562 struct snd_kcontrol_new *wc, int numkc)
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530563{
564 w->id = id;
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530565 w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
566 if (!w->name)
567 return -ENOMEM;
568
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530569 w->sname = stream;
570 w->reg = SND_SOC_NOPM;
571 w->shift = 0;
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530572 w->kcontrol_news = wc;
573 w->num_kcontrols = numkc;
574 w->priv = priv;
575
576 return 0;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530577}
578
579static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530580 const char *sink, const char *control, const char *src,
581 int (*handler)(struct snd_soc_dapm_widget *src,
582 struct snd_soc_dapm_widget *sink))
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530583{
584 route->sink = sink;
585 route->source = src;
586 route->control = control;
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530587 route->connected = handler;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530588}
589
Jeeja KP4a3478d2016-02-12 07:46:06 +0530590static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
591 struct hdac_hdmi_pin *pin)
592{
593 struct hdac_hdmi_priv *hdmi = edev->private_data;
594 struct hdac_hdmi_pcm *pcm = NULL;
595
596 list_for_each_entry(pcm, &hdmi->pcm_list, head) {
597 if (pcm->pin == pin)
598 return pcm;
599 }
600
601 return NULL;
602}
603
604/*
605 * Based on user selection, map the PINs with the PCMs.
606 */
607static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
608 struct snd_ctl_elem_value *ucontrol)
609{
610 int ret;
611 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
612 struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
613 struct snd_soc_dapm_context *dapm = w->dapm;
614 struct hdac_hdmi_pin *pin = w->priv;
615 struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
616 struct hdac_hdmi_priv *hdmi = edev->private_data;
617 struct hdac_hdmi_pcm *pcm = NULL;
618 const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
619
620 ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
621 if (ret < 0)
622 return ret;
623
624 mutex_lock(&hdmi->pin_mutex);
625 list_for_each_entry(pcm, &hdmi->pcm_list, head) {
626 if (pcm->pin == pin)
627 pcm->pin = NULL;
628
629 /*
630 * Jack status is not reported during device probe as the
631 * PCMs are not registered by then. So report it here.
632 */
633 if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
634 pcm->pin = pin;
635 if (pin->eld.monitor_present && pin->eld.eld_valid) {
636 dev_dbg(&edev->hdac.dev,
637 "jack report for pcm=%d\n",
638 pcm->pcm_id);
639
640 snd_jack_report(pcm->jack, SND_JACK_AVOUT);
641 }
642 mutex_unlock(&hdmi->pin_mutex);
643 return ret;
644 }
645 }
646 mutex_unlock(&hdmi->pin_mutex);
647
648 return ret;
649}
650
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530651/*
652 * Ideally the Mux inputs should be based on the num_muxs enumerated, but
653 * the display driver seem to be programming the connection list for the pin
654 * widget runtime.
655 *
656 * So programming all the possible inputs for the mux, the user has to take
657 * care of selecting the right one and leaving all other inputs selected to
658 * "NONE"
659 */
660static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
661 struct hdac_hdmi_pin *pin,
662 struct snd_soc_dapm_widget *widget,
663 const char *widget_name)
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530664{
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530665 struct hdac_hdmi_priv *hdmi = edev->private_data;
666 struct snd_kcontrol_new *kc;
667 struct hdac_hdmi_cvt *cvt;
668 struct soc_enum *se;
669 char kc_name[NAME_SIZE];
670 char mux_items[NAME_SIZE];
671 /* To hold inputs to the Pin mux */
672 char *items[HDA_MAX_CONNECTIONS];
673 int i = 0;
674 int num_items = hdmi->num_cvt + 1;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530675
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530676 kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
677 if (!kc)
678 return -ENOMEM;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530679
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530680 se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
681 if (!se)
682 return -ENOMEM;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530683
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530684 sprintf(kc_name, "Pin %d Input", pin->nid);
685 kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
686 if (!kc->name)
687 return -ENOMEM;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530688
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530689 kc->private_value = (long)se;
690 kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
691 kc->access = 0;
692 kc->info = snd_soc_info_enum_double;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530693 kc->put = hdac_hdmi_set_pin_mux;
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530694 kc->get = snd_soc_dapm_get_enum_double;
695
696 se->reg = SND_SOC_NOPM;
697
698 /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
699 se->items = num_items;
700 se->mask = roundup_pow_of_two(se->items) - 1;
701
702 sprintf(mux_items, "NONE");
703 items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
704 if (!items[i])
705 return -ENOMEM;
706
707 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
708 i++;
709 sprintf(mux_items, "cvt %d", cvt->nid);
710 items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
711 if (!items[i])
712 return -ENOMEM;
713 }
714
715 se->texts = devm_kmemdup(&edev->hdac.dev, items,
716 (num_items * sizeof(char *)), GFP_KERNEL);
717 if (!se->texts)
718 return -ENOMEM;
719
720 return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
Jeeja KP4a3478d2016-02-12 07:46:06 +0530721 snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +0530722}
723
724/* Add cvt <- input <- mux route map */
725static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
726 struct snd_soc_dapm_widget *widgets,
727 struct snd_soc_dapm_route *route, int rindex)
728{
729 struct hdac_hdmi_priv *hdmi = edev->private_data;
730 const struct snd_kcontrol_new *kc;
731 struct soc_enum *se;
732 int mux_index = hdmi->num_cvt + hdmi->num_pin;
733 int i, j;
734
735 for (i = 0; i < hdmi->num_pin; i++) {
736 kc = widgets[mux_index].kcontrol_news;
737 se = (struct soc_enum *)kc->private_value;
738 for (j = 0; j < hdmi->num_cvt; j++) {
739 hdac_hdmi_fill_route(&route[rindex],
740 widgets[mux_index].name,
741 se->texts[j + 1],
742 widgets[j].name, NULL);
743
744 rindex++;
745 }
746
747 mux_index++;
748 }
749}
750
751/*
752 * Widgets are added in the below sequence
753 * Converter widgets for num converters enumerated
754 * Pin widgets for num pins enumerated
755 * Pin mux widgets to represent connenction list of pin widget
756 *
757 * Total widgets elements = num_cvt + num_pin + num_pin;
758 *
759 * Routes are added as below:
760 * pin mux -> pin (based on num_pins)
761 * cvt -> "Input sel control" -> pin_mux
762 *
763 * Total route elements:
764 * num_pins + (pin_muxes * num_cvt)
765 */
766static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
767{
768 struct snd_soc_dapm_widget *widgets;
769 struct snd_soc_dapm_route *route;
770 struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
771 struct hdac_hdmi_priv *hdmi = edev->private_data;
772 struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
773 char widget_name[NAME_SIZE];
774 struct hdac_hdmi_cvt *cvt;
775 struct hdac_hdmi_pin *pin;
776 int ret, i = 0, num_routes = 0;
777
778 if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
779 return -EINVAL;
780
781 widgets = devm_kzalloc(dapm->dev,
782 (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
783 GFP_KERNEL);
784
785 if (!widgets)
786 return -ENOMEM;
787
788 /* DAPM widgets to represent each converter widget */
789 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
790 sprintf(widget_name, "Converter %d", cvt->nid);
791 ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
792 snd_soc_dapm_aif_in, &cvt->nid,
793 widget_name, dai_drv[i].playback.stream_name, NULL, 0);
794 if (ret < 0)
795 return ret;
796 i++;
797 }
798
799 list_for_each_entry(pin, &hdmi->pin_list, head) {
800 sprintf(widget_name, "hif%d Output", pin->nid);
801 ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
802 snd_soc_dapm_output, &pin->nid,
803 widget_name, NULL, NULL, 0);
804 if (ret < 0)
805 return ret;
806 i++;
807 }
808
809 /* DAPM widgets to represent the connection list to pin widget */
810 list_for_each_entry(pin, &hdmi->pin_list, head) {
811 sprintf(widget_name, "Pin %d Mux", pin->nid);
812 ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
813 widget_name);
814 if (ret < 0)
815 return ret;
816 i++;
817
818 /* For cvt to pin_mux mapping */
819 num_routes += hdmi->num_cvt;
820
821 /* For pin_mux to pin mapping */
822 num_routes++;
823 }
824
825 route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
826 GFP_KERNEL);
827 if (!route)
828 return -ENOMEM;
829
830 i = 0;
831 /* Add pin <- NULL <- mux route map */
832 list_for_each_entry(pin, &hdmi->pin_list, head) {
833 int sink_index = i + hdmi->num_cvt;
834 int src_index = sink_index + hdmi->num_pin;
835
836 hdac_hdmi_fill_route(&route[i],
837 widgets[sink_index].name, NULL,
838 widgets[src_index].name, NULL);
839 i++;
840
841 }
842
843 hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
844
845 snd_soc_dapm_new_controls(dapm, widgets,
846 ((2 * hdmi->num_pin) + hdmi->num_cvt));
847
848 snd_soc_dapm_add_routes(dapm, route, num_routes);
849 snd_soc_dapm_new_widgets(dapm->card);
850
851 return 0;
852
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530853}
854
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530855static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530856{
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530857 struct hdac_hdmi_priv *hdmi = edev->private_data;
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530858 struct hdac_hdmi_dai_pin_map *dai_map;
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530859 struct hdac_hdmi_cvt *cvt;
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530860 int dai_id = 0;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530861
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530862 if (list_empty(&hdmi->cvt_list))
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530863 return -EINVAL;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530864
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530865 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
866 dai_map = &hdmi->dai_map[dai_id];
867 dai_map->dai_id = dai_id;
868 dai_map->cvt = cvt;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530869
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530870 /* Enable transmission */
871 snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
872 AC_VERB_SET_DIGI_CONVERT_1, 1);
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530873
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530874 /* Category Code (CC) to zero */
875 snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
876 AC_VERB_SET_DIGI_CONVERT_2, 0);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530877
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530878 dai_id++;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530879
Subhransu S. Prusty148569f2016-02-12 07:46:07 +0530880 if (dai_id == HDA_MAX_CVTS) {
881 dev_warn(&edev->hdac.dev,
882 "Max dais supported: %d\n", dai_id);
883 break;
884 }
885 }
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530886
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530887 return 0;
888}
889
890static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
891{
892 struct hdac_hdmi_priv *hdmi = edev->private_data;
893 struct hdac_hdmi_cvt *cvt;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530894 char name[NAME_SIZE];
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530895
896 cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
897 if (!cvt)
898 return -ENOMEM;
899
900 cvt->nid = nid;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530901 sprintf(name, "cvt %d", cvt->nid);
902 cvt->name = kstrdup(name, GFP_KERNEL);
Subhransu S. Prusty15b91442015-12-09 21:46:10 +0530903
904 list_add_tail(&cvt->head, &hdmi->cvt_list);
905 hdmi->num_cvt++;
906
907 return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
908}
909
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530910static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
911{
912 struct hdac_ext_device *edev = pin->edev;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530913 struct hdac_hdmi_priv *hdmi = edev->private_data;
914 struct hdac_hdmi_pcm *pcm;
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530915 int val;
916
917 if (!edev)
918 return;
919
920 pin->repoll_count = repoll;
921
922 pm_runtime_get_sync(&edev->hdac.dev);
923 val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
924 AC_VERB_GET_PIN_SENSE, 0);
925
926 dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
927 val, pin->nid);
928
Jeeja KP4a3478d2016-02-12 07:46:06 +0530929
930 mutex_lock(&hdmi->pin_mutex);
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530931 pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
932 pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
933
Jeeja KP4a3478d2016-02-12 07:46:06 +0530934 pcm = hdac_hdmi_get_pcm(edev, pin);
935
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530936 if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
937
938 dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
939 __func__, pin->nid);
Jeeja KP4a3478d2016-02-12 07:46:06 +0530940
941 /*
942 * PCMs are not registered during device probe, so don't
943 * report jack here. It will be done in usermode mux
944 * control select.
945 */
946 if (pcm) {
947 dev_dbg(&edev->hdac.dev,
948 "jack report for pcm=%d\n", pcm->pcm_id);
949
950 snd_jack_report(pcm->jack, 0);
951 }
952
953 mutex_unlock(&hdmi->pin_mutex);
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530954 goto put_hdac_device;
955 }
956
957 if (pin->eld.monitor_present && pin->eld.eld_valid) {
958 /* TODO: use i915 component for reading ELD later */
959 if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
960 pin->eld.eld_buffer,
961 &pin->eld.eld_size) == 0) {
962
Jeeja KP4a3478d2016-02-12 07:46:06 +0530963 if (pcm) {
964 dev_dbg(&edev->hdac.dev,
965 "jack report for pcm=%d\n",
966 pcm->pcm_id);
967
968 snd_jack_report(pcm->jack, SND_JACK_AVOUT);
969 }
970
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530971 print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
972 pin->eld.eld_buffer, pin->eld.eld_size);
973 } else {
974 pin->eld.monitor_present = false;
975 pin->eld.eld_valid = false;
Jeeja KP4a3478d2016-02-12 07:46:06 +0530976
977 if (pcm) {
978 dev_dbg(&edev->hdac.dev,
979 "jack report for pcm=%d\n",
980 pcm->pcm_id);
981
982 snd_jack_report(pcm->jack, 0);
983 }
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530984 }
985 }
986
Jeeja KP4a3478d2016-02-12 07:46:06 +0530987 mutex_unlock(&hdmi->pin_mutex);
988
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +0530989 /*
990 * Sometimes the pin_sense may present invalid monitor
991 * present and eld_valid. If ELD data is not valid, loop few
992 * more times to get correct pin sense and valid ELD.
993 */
994 if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
995 schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
996
997put_hdac_device:
998 pm_runtime_put_sync(&edev->hdac.dev);
999}
1000
1001static void hdac_hdmi_repoll_eld(struct work_struct *work)
1002{
1003 struct hdac_hdmi_pin *pin =
1004 container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
1005
1006 /* picked from legacy HDA driver */
1007 if (pin->repoll_count++ > 6)
1008 pin->repoll_count = 0;
1009
1010 hdac_hdmi_present_sense(pin, pin->repoll_count);
1011}
1012
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301013static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
1014{
1015 struct hdac_hdmi_priv *hdmi = edev->private_data;
1016 struct hdac_hdmi_pin *pin;
1017
1018 pin = kzalloc(sizeof(*pin), GFP_KERNEL);
1019 if (!pin)
1020 return -ENOMEM;
1021
1022 pin->nid = nid;
1023
1024 list_add_tail(&pin->head, &hdmi->pin_list);
1025 hdmi->num_pin++;
1026
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +05301027 pin->edev = edev;
1028 INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
1029
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301030 return 0;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301031}
1032
Subhransu S. Prusty211caab2016-02-12 07:46:03 +05301033#define INTEL_VENDOR_NID 0x08
1034#define INTEL_GET_VENDOR_VERB 0xf81
1035#define INTEL_SET_VENDOR_VERB 0x781
1036#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
1037#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
1038
1039static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
1040{
1041 unsigned int vendor_param;
1042
1043 vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
1044 INTEL_GET_VENDOR_VERB, 0);
1045 if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
1046 return;
1047
1048 vendor_param |= INTEL_EN_ALL_PIN_CVTS;
1049 vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
1050 INTEL_SET_VENDOR_VERB, vendor_param);
1051 if (vendor_param == -1)
1052 return;
1053}
1054
1055static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
1056{
1057 unsigned int vendor_param;
1058
1059 vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
1060 INTEL_GET_VENDOR_VERB, 0);
1061 if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
1062 return;
1063
1064 /* enable DP1.2 mode */
1065 vendor_param |= INTEL_EN_DP12;
1066 vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
1067 INTEL_SET_VENDOR_VERB, vendor_param);
1068 if (vendor_param == -1)
1069 return;
1070
1071}
1072
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301073static struct snd_soc_dai_ops hdmi_dai_ops = {
1074 .startup = hdac_hdmi_pcm_open,
1075 .shutdown = hdac_hdmi_pcm_close,
1076 .hw_params = hdac_hdmi_set_hw_params,
1077 .prepare = hdac_hdmi_playback_prepare,
1078 .hw_free = hdac_hdmi_playback_cleanup,
1079};
1080
1081/*
1082 * Each converter can support a stream independently. So a dai is created
1083 * based on the number of converter queried.
1084 */
1085static int hdac_hdmi_create_dais(struct hdac_device *hdac,
1086 struct snd_soc_dai_driver **dais,
1087 struct hdac_hdmi_priv *hdmi, int num_dais)
1088{
1089 struct snd_soc_dai_driver *hdmi_dais;
1090 struct hdac_hdmi_cvt *cvt;
1091 char name[NAME_SIZE], dai_name[NAME_SIZE];
1092 int i = 0;
1093 u32 rates, bps;
1094 unsigned int rate_max = 384000, rate_min = 8000;
1095 u64 formats;
1096 int ret;
1097
1098 hdmi_dais = devm_kzalloc(&hdac->dev,
1099 (sizeof(*hdmi_dais) * num_dais),
1100 GFP_KERNEL);
1101 if (!hdmi_dais)
1102 return -ENOMEM;
1103
1104 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
1105 ret = snd_hdac_query_supported_pcm(hdac, cvt->nid,
1106 &rates, &formats, &bps);
1107 if (ret)
1108 return ret;
1109
1110 sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
1111 hdmi_dais[i].name = devm_kstrdup(&hdac->dev,
1112 dai_name, GFP_KERNEL);
1113
1114 if (!hdmi_dais[i].name)
1115 return -ENOMEM;
1116
1117 snprintf(name, sizeof(name), "hifi%d", i+1);
1118 hdmi_dais[i].playback.stream_name =
1119 devm_kstrdup(&hdac->dev, name, GFP_KERNEL);
1120 if (!hdmi_dais[i].playback.stream_name)
1121 return -ENOMEM;
1122
1123 /*
1124 * Set caps based on capability queried from the converter.
1125 * It will be constrained runtime based on ELD queried.
1126 */
1127 hdmi_dais[i].playback.formats = formats;
1128 hdmi_dais[i].playback.rates = rates;
1129 hdmi_dais[i].playback.rate_max = rate_max;
1130 hdmi_dais[i].playback.rate_min = rate_min;
1131 hdmi_dais[i].playback.channels_min = 2;
1132 hdmi_dais[i].playback.channels_max = 2;
1133 hdmi_dais[i].ops = &hdmi_dai_ops;
1134
1135 i++;
1136 }
1137
1138 *dais = hdmi_dais;
1139
1140 return 0;
1141}
1142
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301143/*
1144 * Parse all nodes and store the cvt/pin nids in array
1145 * Add one time initialization for pin and cvt widgets
1146 */
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301147static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
1148 struct snd_soc_dai_driver **dais, int *num_dais)
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301149{
1150 hda_nid_t nid;
Sudip Mukherjee3c83ac22015-12-01 14:29:35 +05301151 int i, num_nodes;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301152 struct hdac_device *hdac = &edev->hdac;
1153 struct hdac_hdmi_priv *hdmi = edev->private_data;
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301154 int ret;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301155
Subhransu S. Prusty211caab2016-02-12 07:46:03 +05301156 hdac_hdmi_skl_enable_all_pins(hdac);
1157 hdac_hdmi_skl_enable_dp12(hdac);
1158
Sudip Mukherjee3c83ac22015-12-01 14:29:35 +05301159 num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
Subhransu S. Prusty541140d2015-12-09 21:46:08 +05301160 if (!nid || num_nodes <= 0) {
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301161 dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
1162 return -EINVAL;
1163 }
1164
Sudip Mukherjee3c83ac22015-12-01 14:29:35 +05301165 hdac->num_nodes = num_nodes;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301166 hdac->start_nid = nid;
1167
1168 for (i = 0; i < hdac->num_nodes; i++, nid++) {
1169 unsigned int caps;
1170 unsigned int type;
1171
1172 caps = get_wcaps(hdac, nid);
1173 type = get_wcaps_type(caps);
1174
1175 if (!(caps & AC_WCAP_DIGITAL))
1176 continue;
1177
1178 switch (type) {
1179
1180 case AC_WID_AUD_OUT:
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301181 ret = hdac_hdmi_add_cvt(edev, nid);
1182 if (ret < 0)
1183 return ret;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301184 break;
1185
1186 case AC_WID_PIN:
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301187 ret = hdac_hdmi_add_pin(edev, nid);
1188 if (ret < 0)
1189 return ret;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301190 break;
1191 }
1192 }
1193
1194 hdac->end_nid = nid;
1195
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301196 if (!hdmi->num_pin || !hdmi->num_cvt)
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301197 return -EIO;
1198
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301199 ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt);
1200 if (ret) {
1201 dev_err(&hdac->dev, "Failed to create dais with err: %d\n",
1202 ret);
1203 return ret;
1204 }
1205
1206 *num_dais = hdmi->num_cvt;
1207
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301208 return hdac_hdmi_init_dai_map(edev);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301209}
1210
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +05301211static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
1212{
1213 struct hdac_ext_device *edev = aptr;
1214 struct hdac_hdmi_priv *hdmi = edev->private_data;
1215 struct hdac_hdmi_pin *pin;
1216 struct snd_soc_codec *codec = edev->scodec;
1217
1218 /* Don't know how this mapping is derived */
1219 hda_nid_t pin_nid = port + 0x04;
1220
1221 dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
1222
1223 /*
1224 * skip notification during system suspend (but not in runtime PM);
1225 * the state will be updated at resume. Also since the ELD and
1226 * connection states are updated in anyway at the end of the resume,
1227 * we can skip it when received during PM process.
1228 */
1229 if (snd_power_get_state(codec->component.card->snd_card) !=
1230 SNDRV_CTL_POWER_D0)
1231 return;
1232
1233 if (atomic_read(&edev->hdac.in_pm))
1234 return;
1235
1236 list_for_each_entry(pin, &hdmi->pin_list, head) {
1237 if (pin->nid == pin_nid)
1238 hdac_hdmi_present_sense(pin, 1);
1239 }
1240}
1241
1242static struct i915_audio_component_audio_ops aops = {
1243 .pin_eld_notify = hdac_hdmi_eld_notify_cb,
1244};
1245
Jeeja KP4a3478d2016-02-12 07:46:06 +05301246int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
1247{
1248 char jack_name[NAME_SIZE];
1249 struct snd_soc_codec *codec = dai->codec;
1250 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
1251 struct snd_soc_dapm_context *dapm =
1252 snd_soc_component_get_dapm(&codec->component);
1253 struct hdac_hdmi_priv *hdmi = edev->private_data;
1254 struct hdac_hdmi_pcm *pcm;
1255
1256 /*
1257 * this is a new PCM device, create new pcm and
1258 * add to the pcm list
1259 */
1260 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
1261 if (!pcm)
1262 return -ENOMEM;
1263 pcm->pcm_id = device;
1264 pcm->cvt = hdmi->dai_map[dai->id].cvt;
1265
1266 list_add_tail(&pcm->head, &hdmi->pcm_list);
1267
1268 sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
1269
1270 return snd_jack_new(dapm->card->snd_card, jack_name,
1271 SND_JACK_AVOUT, &pcm->jack, true, false);
1272}
1273EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
1274
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301275static int hdmi_codec_probe(struct snd_soc_codec *codec)
1276{
1277 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
1278 struct hdac_hdmi_priv *hdmi = edev->private_data;
1279 struct snd_soc_dapm_context *dapm =
1280 snd_soc_component_get_dapm(&codec->component);
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +05301281 struct hdac_hdmi_pin *pin;
1282 int ret;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301283
1284 edev->scodec = codec;
1285
Subhransu S. Prusty79f4e922016-02-12 07:46:05 +05301286 ret = create_fill_widget_route_map(dapm);
1287 if (ret < 0)
1288 return ret;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301289
Subhransu S. Prustyb8a54542016-02-12 07:46:01 +05301290 aops.audio_ptr = edev;
1291 ret = snd_hdac_i915_register_notifier(&aops);
1292 if (ret < 0) {
1293 dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
1294 ret);
1295 return ret;
1296 }
1297
1298 list_for_each_entry(pin, &hdmi->pin_list, head)
1299 hdac_hdmi_present_sense(pin, 1);
1300
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301301 /* Imp: Store the card pointer in hda_codec */
1302 edev->card = dapm->card->snd_card;
1303
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301304 /*
1305 * hdac_device core already sets the state to active and calls
1306 * get_noresume. So enable runtime and set the device to suspend.
1307 */
1308 pm_runtime_enable(&edev->hdac.dev);
1309 pm_runtime_put(&edev->hdac.dev);
1310 pm_runtime_suspend(&edev->hdac.dev);
1311
1312 return 0;
1313}
1314
1315static int hdmi_codec_remove(struct snd_soc_codec *codec)
1316{
1317 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
1318
1319 pm_runtime_disable(&edev->hdac.dev);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301320 return 0;
1321}
1322
1323static struct snd_soc_codec_driver hdmi_hda_codec = {
1324 .probe = hdmi_codec_probe,
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301325 .remove = hdmi_codec_remove,
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301326 .idle_bias_off = true,
1327};
1328
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301329static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
1330{
1331 struct hdac_device *codec = &edev->hdac;
1332 struct hdac_hdmi_priv *hdmi_priv;
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301333 struct snd_soc_dai_driver *hdmi_dais = NULL;
1334 int num_dais = 0;
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301335 int ret = 0;
1336
1337 hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
1338 if (hdmi_priv == NULL)
1339 return -ENOMEM;
1340
1341 edev->private_data = hdmi_priv;
1342
1343 dev_set_drvdata(&codec->dev, edev);
1344
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301345 INIT_LIST_HEAD(&hdmi_priv->pin_list);
1346 INIT_LIST_HEAD(&hdmi_priv->cvt_list);
Jeeja KP4a3478d2016-02-12 07:46:06 +05301347 INIT_LIST_HEAD(&hdmi_priv->pcm_list);
1348 mutex_init(&hdmi_priv->pin_mutex);
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301349
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301350 ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
1351 if (ret < 0) {
1352 dev_err(&codec->dev,
1353 "Failed in parse and map nid with err: %d\n", ret);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301354 return ret;
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301355 }
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301356
1357 /* ASoC specific initialization */
1358 return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
Subhransu S. Prusty17a42c42016-02-12 07:46:04 +05301359 hdmi_dais, num_dais);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301360}
1361
1362static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
1363{
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301364 struct hdac_hdmi_priv *hdmi = edev->private_data;
1365 struct hdac_hdmi_pin *pin, *pin_next;
1366 struct hdac_hdmi_cvt *cvt, *cvt_next;
Jeeja KP4a3478d2016-02-12 07:46:06 +05301367 struct hdac_hdmi_pcm *pcm, *pcm_next;
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301368
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301369 snd_soc_unregister_codec(&edev->hdac.dev);
1370
Jeeja KP4a3478d2016-02-12 07:46:06 +05301371 list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
1372 pcm->cvt = NULL;
1373 pcm->pin = NULL;
1374 list_del(&pcm->head);
1375 kfree(pcm);
1376 }
1377
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301378 list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
1379 list_del(&cvt->head);
Jeeja KP4a3478d2016-02-12 07:46:06 +05301380 kfree(cvt->name);
Subhransu S. Prusty15b91442015-12-09 21:46:10 +05301381 kfree(cvt);
1382 }
1383
1384 list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
1385 list_del(&pin->head);
1386 kfree(pin);
1387 }
1388
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301389 return 0;
1390}
1391
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301392#ifdef CONFIG_PM
1393static int hdac_hdmi_runtime_suspend(struct device *dev)
1394{
1395 struct hdac_ext_device *edev = to_hda_ext_device(dev);
1396 struct hdac_device *hdac = &edev->hdac;
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +05301397 struct hdac_bus *bus = hdac->bus;
1398 int err;
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301399
1400 dev_dbg(dev, "Enter: %s\n", __func__);
1401
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +05301402 /* controller may not have been initialized for the first time */
1403 if (!bus)
1404 return 0;
1405
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301406 /* Power down afg */
1407 if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
1408 snd_hdac_codec_write(hdac, hdac->afg, 0,
1409 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
1410
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +05301411 err = snd_hdac_display_power(bus, false);
1412 if (err < 0) {
1413 dev_err(bus->dev, "Cannot turn on display power on i915\n");
1414 return err;
1415 }
1416
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301417 return 0;
1418}
1419
1420static int hdac_hdmi_runtime_resume(struct device *dev)
1421{
1422 struct hdac_ext_device *edev = to_hda_ext_device(dev);
1423 struct hdac_device *hdac = &edev->hdac;
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +05301424 struct hdac_bus *bus = hdac->bus;
1425 int err;
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301426
1427 dev_dbg(dev, "Enter: %s\n", __func__);
1428
Subhransu S. Prusty07f083a2015-11-10 18:42:10 +05301429 /* controller may not have been initialized for the first time */
1430 if (!bus)
1431 return 0;
1432
1433 err = snd_hdac_display_power(bus, true);
1434 if (err < 0) {
1435 dev_err(bus->dev, "Cannot turn on display power on i915\n");
1436 return err;
1437 }
1438
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301439 /* Power up afg */
1440 if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
1441 snd_hdac_codec_write(hdac, hdac->afg, 0,
1442 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1443
1444 return 0;
1445}
1446#else
1447#define hdac_hdmi_runtime_suspend NULL
1448#define hdac_hdmi_runtime_resume NULL
1449#endif
1450
1451static const struct dev_pm_ops hdac_hdmi_pm = {
1452 SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
1453};
1454
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301455static const struct hda_device_id hdmi_list[] = {
1456 HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
1457 {}
1458};
1459
1460MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
1461
1462static struct hdac_ext_driver hdmi_driver = {
1463 . hdac = {
1464 .driver = {
1465 .name = "HDMI HDA Codec",
Subhransu S. Prustye342ac02015-11-10 18:42:07 +05301466 .pm = &hdac_hdmi_pm,
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301467 },
1468 .id_table = hdmi_list,
1469 },
1470 .probe = hdac_hdmi_dev_probe,
1471 .remove = hdac_hdmi_dev_remove,
1472};
1473
1474static int __init hdmi_init(void)
1475{
1476 return snd_hda_ext_driver_register(&hdmi_driver);
1477}
1478
1479static void __exit hdmi_exit(void)
1480{
1481 snd_hda_ext_driver_unregister(&hdmi_driver);
1482}
1483
1484module_init(hdmi_init);
1485module_exit(hdmi_exit);
1486
1487MODULE_LICENSE("GPL v2");
1488MODULE_DESCRIPTION("HDMI HD codec");
1489MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
1490MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");