aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSubhransu S. Prusty <subhransu.s.prusty@intel.com>2016-02-11 21:16:01 -0500
committerMark Brown <broonie@kernel.org>2016-02-15 15:11:09 -0500
commitb8a54545b00cbf2aff743578c5c69aafdcde1d64 (patch)
tree8908e8ae3e6350be00631fcb492b1ec181fd13b6
parent92e963f50fc74041b5e9e744c330dca48e04f08d (diff)
ASoC: hdac_hdmi: Add hotplug notification and read ELD
This patch uses i915 component framework to register for hotplug notification. In the hotplug notification, driver reads pin sense and ELD by sending PIN_SENSE and ELD verbs over HDA bus. Once it identifies valid pin sense and valid ELD, store the ELD into the corresponding pin map buffer. Also read the monitor present sense during resume and ignore the ELD notify from graphics during PM as is done in legacy hda, commit 8ae743e82f0b ("ALSA: hda - Skip ELD notification during system suspend") Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/hdac_hdmi.c217
1 files changed, 211 insertions, 6 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 5a1ec0f7a1a6..fff225d6539c 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -34,6 +34,9 @@
34 34
35#define HDA_MAX_CONNECTIONS 32 35#define HDA_MAX_CONNECTIONS 32
36 36
37#define ELD_MAX_SIZE 256
38#define ELD_FIXED_BYTES 20
39
37struct hdac_hdmi_cvt_params { 40struct hdac_hdmi_cvt_params {
38 unsigned int channels_min; 41 unsigned int channels_min;
39 unsigned int channels_max; 42 unsigned int channels_max;
@@ -48,11 +51,22 @@ struct hdac_hdmi_cvt {
48 struct hdac_hdmi_cvt_params params; 51 struct hdac_hdmi_cvt_params params;
49}; 52};
50 53
54struct hdac_hdmi_eld {
55 bool monitor_present;
56 bool eld_valid;
57 int eld_size;
58 char eld_buffer[ELD_MAX_SIZE];
59};
60
51struct hdac_hdmi_pin { 61struct hdac_hdmi_pin {
52 struct list_head head; 62 struct list_head head;
53 hda_nid_t nid; 63 hda_nid_t nid;
54 int num_mux_nids; 64 int num_mux_nids;
55 hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; 65 hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
66 struct hdac_hdmi_eld eld;
67 struct hdac_ext_device *edev;
68 int repoll_count;
69 struct delayed_work work;
56}; 70};
57 71
58struct hdac_hdmi_dai_pin_map { 72struct hdac_hdmi_dai_pin_map {
@@ -76,6 +90,80 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
76 return to_ehdac_device(hdac); 90 return to_ehdac_device(hdac);
77} 91}
78 92
93 /* HDMI ELD routines */
94static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
95 hda_nid_t nid, int byte_index)
96{
97 unsigned int val;
98
99 val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
100 byte_index);
101
102 dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
103 byte_index, val);
104
105 return val;
106}
107
108static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
109{
110 return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
111 AC_DIPSIZE_ELD_BUF);
112}
113
114/*
115 * This function queries the ELD size and ELD data and fills in the buffer
116 * passed by user
117 */
118static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
119 unsigned char *buf, int *eld_size)
120{
121 int i, size, ret = 0;
122
123 /*
124 * ELD size is initialized to zero in caller function. If no errors and
125 * ELD is valid, actual eld_size is assigned.
126 */
127
128 size = hdac_hdmi_get_eld_size(codec, nid);
129 if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
130 dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
131 return -ERANGE;
132 }
133
134 /* set ELD buffer */
135 for (i = 0; i < size; i++) {
136 unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
137 /*
138 * Graphics driver might be writing to ELD buffer right now.
139 * Just abort. The caller will repoll after a while.
140 */
141 if (!(val & AC_ELDD_ELD_VALID)) {
142 dev_err(&codec->dev,
143 "HDMI: invalid ELD data byte %d\n", i);
144 ret = -EINVAL;
145 goto error;
146 }
147 val &= AC_ELDD_ELD_DATA;
148 /*
149 * The first byte cannot be zero. This can happen on some DVI
150 * connections. Some Intel chips may also need some 250ms delay
151 * to return non-zero ELD data, even when the graphics driver
152 * correctly writes ELD content before setting ELD_valid bit.
153 */
154 if (!val && !i) {
155 dev_err(&codec->dev, "HDMI: 0 ELD data\n");
156 ret = -EINVAL;
157 goto error;
158 }
159 buf[i] = val;
160 }
161
162 *eld_size = size;
163error:
164 return ret;
165}
166
79static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, 167static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
80 hda_nid_t cvt_nid, hda_nid_t pin_nid, 168 hda_nid_t cvt_nid, hda_nid_t pin_nid,
81 u32 stream_tag, int format) 169 u32 stream_tag, int format)
@@ -246,7 +334,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
246 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); 334 struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
247 struct hdac_hdmi_priv *hdmi = hdac->private_data; 335 struct hdac_hdmi_priv *hdmi = hdac->private_data;
248 struct hdac_hdmi_dai_pin_map *dai_map; 336 struct hdac_hdmi_dai_pin_map *dai_map;
249 int val;
250 337
251 if (dai->id > 0) { 338 if (dai->id > 0) {
252 dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); 339 dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
@@ -255,12 +342,14 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
255 342
256 dai_map = &hdmi->dai_map[dai->id]; 343 dai_map = &hdmi->dai_map[dai->id];
257 344
258 val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0, 345 if ((!dai_map->pin->eld.monitor_present) ||
259 AC_VERB_GET_PIN_SENSE, 0); 346 (!dai_map->pin->eld.eld_valid)) {
260 dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val); 347
348 dev_err(&hdac->hdac.dev,
349 "Failed: montior present? %d ELD valid?: %d\n",
350 dai_map->pin->eld.monitor_present,
351 dai_map->pin->eld.eld_valid);
261 352
262 if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
263 dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
264 return -ENODEV; 353 return -ENODEV;
265 } 354 }
266 355
@@ -410,6 +499,71 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
410 return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); 499 return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
411} 500}
412 501
502static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
503{
504 struct hdac_ext_device *edev = pin->edev;
505 int val;
506
507 if (!edev)
508 return;
509
510 pin->repoll_count = repoll;
511
512 pm_runtime_get_sync(&edev->hdac.dev);
513 val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
514 AC_VERB_GET_PIN_SENSE, 0);
515
516 dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
517 val, pin->nid);
518
519 pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
520 pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
521
522 if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
523
524 dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
525 __func__, pin->nid);
526 goto put_hdac_device;
527 }
528
529 if (pin->eld.monitor_present && pin->eld.eld_valid) {
530 /* TODO: use i915 component for reading ELD later */
531 if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
532 pin->eld.eld_buffer,
533 &pin->eld.eld_size) == 0) {
534
535 print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
536 pin->eld.eld_buffer, pin->eld.eld_size);
537 } else {
538 pin->eld.monitor_present = false;
539 pin->eld.eld_valid = false;
540 }
541 }
542
543 /*
544 * Sometimes the pin_sense may present invalid monitor
545 * present and eld_valid. If ELD data is not valid, loop few
546 * more times to get correct pin sense and valid ELD.
547 */
548 if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
549 schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
550
551put_hdac_device:
552 pm_runtime_put_sync(&edev->hdac.dev);
553}
554
555static void hdac_hdmi_repoll_eld(struct work_struct *work)
556{
557 struct hdac_hdmi_pin *pin =
558 container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
559
560 /* picked from legacy HDA driver */
561 if (pin->repoll_count++ > 6)
562 pin->repoll_count = 0;
563
564 hdac_hdmi_present_sense(pin, pin->repoll_count);
565}
566
413static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) 567static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
414{ 568{
415 struct hdac_hdmi_priv *hdmi = edev->private_data; 569 struct hdac_hdmi_priv *hdmi = edev->private_data;
@@ -424,6 +578,9 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
424 list_add_tail(&pin->head, &hdmi->pin_list); 578 list_add_tail(&pin->head, &hdmi->pin_list);
425 hdmi->num_pin++; 579 hdmi->num_pin++;
426 580
581 pin->edev = edev;
582 INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
583
427 return 0; 584 return 0;
428} 585}
429 586
@@ -482,17 +639,65 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
482 return hdac_hdmi_init_dai_map(edev); 639 return hdac_hdmi_init_dai_map(edev);
483} 640}
484 641
642static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
643{
644 struct hdac_ext_device *edev = aptr;
645 struct hdac_hdmi_priv *hdmi = edev->private_data;
646 struct hdac_hdmi_pin *pin;
647 struct snd_soc_codec *codec = edev->scodec;
648
649 /* Don't know how this mapping is derived */
650 hda_nid_t pin_nid = port + 0x04;
651
652 dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
653
654 /*
655 * skip notification during system suspend (but not in runtime PM);
656 * the state will be updated at resume. Also since the ELD and
657 * connection states are updated in anyway at the end of the resume,
658 * we can skip it when received during PM process.
659 */
660 if (snd_power_get_state(codec->component.card->snd_card) !=
661 SNDRV_CTL_POWER_D0)
662 return;
663
664 if (atomic_read(&edev->hdac.in_pm))
665 return;
666
667 list_for_each_entry(pin, &hdmi->pin_list, head) {
668 if (pin->nid == pin_nid)
669 hdac_hdmi_present_sense(pin, 1);
670 }
671}
672
673static struct i915_audio_component_audio_ops aops = {
674 .pin_eld_notify = hdac_hdmi_eld_notify_cb,
675};
676
485static int hdmi_codec_probe(struct snd_soc_codec *codec) 677static int hdmi_codec_probe(struct snd_soc_codec *codec)
486{ 678{
487 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); 679 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
488 struct hdac_hdmi_priv *hdmi = edev->private_data; 680 struct hdac_hdmi_priv *hdmi = edev->private_data;
489 struct snd_soc_dapm_context *dapm = 681 struct snd_soc_dapm_context *dapm =
490 snd_soc_component_get_dapm(&codec->component); 682 snd_soc_component_get_dapm(&codec->component);
683 struct hdac_hdmi_pin *pin;
684 int ret;
491 685
492 edev->scodec = codec; 686 edev->scodec = codec;
493 687
494 create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); 688 create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
495 689
690 aops.audio_ptr = edev;
691 ret = snd_hdac_i915_register_notifier(&aops);
692 if (ret < 0) {
693 dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
694 ret);
695 return ret;
696 }
697
698 list_for_each_entry(pin, &hdmi->pin_list, head)
699 hdac_hdmi_present_sense(pin, 1);
700
496 /* Imp: Store the card pointer in hda_codec */ 701 /* Imp: Store the card pointer in hda_codec */
497 edev->card = dapm->card->snd_card; 702 edev->card = dapm->card->snd_card;
498 703