Skip to content

Commit ade49db

Browse files
committed
ALSA: hda/hdmi - Allow audio component for AMD/ATI and Nvidia HDMI
AMD/ATI and Nvidia HDMI codec drivers didn't have the audio component binding like i915, but it worked only with the traditional HD-audio unsolicited event for the HDMI hotplug detection and the ELD read-up thereafter. This has been a problem in many ways: first of all, it goes through the hardware event transition (from GPU register write, HD-audio controller trigger, and finally to HD-audio unsolicited event handling), which is often unreliable and may miss some opportunities. Second, each unsol event handling and ELD read-up need the explicit power up / down when the codec is in the runtime suspend. Last but not least, which is the most important, the hotplug wakeup may be missed when the HD-audio controller is in runtime suspend. Especially the last point is a big problem due to the recent change relevant with vga_switcheroo that forcibly enables the runtime PM for AMD HDMI controllers. These issues are solved by introducing the audio component; the hotplug notification is done by a direct function callback, which is more accurate and reliable, and it can be processed without the actual hardware access, i.e. no runtime PM trigger is needed, and the HD-audio gets the event even if it's in runtime suspend. The same for ELD query, as it's read directly from the cached ELD bytes stored in the DRM driver, hence the whole hardware access can be skipped. So here it is: this patch implements the audio component binding with AMD/ATI and Nouveau DRM drivers. The biggest difference from i915 implementation is that this binding is fully optional and it can be enabled asynchronously on the fly. That is, the driver will switch from the HD-audio unsolicited event to the notify callback once when the DRM component gets bound. Similarly, when DRM driver gets unloaded, the HDMI event handling returns to the legacy mode, too. Also, another difference from i915 is that the new code registers the component in the codec driver, while i915 HDMI codec assumes the component binding was already done in the HD-audio controller driver. Hence the new code does need to de-register the component binding at the codec exit, too. Some other details: - The match component ops assumes that both VGA and HD-audio controller PCI entries belong to the same PCI bus, and only accepts such an entry. - The pin2port audio_ops is implemented with assumption of the fixed widget layout. For AMD, it's starting from 3, with step 2 (3, 5, 7, ...), while for Nvidia, it's starting from 4, with step 1 (4, 5, 6, ...) As of this patch, the corresponding component isn't implemented in DRM side, so this change alone won't give any benefit. By the following changes in DRM sides, the mission will be completed. Signed-off-by: Takashi Iwai <[email protected]>
1 parent 551626e commit ade49db

File tree

1 file changed

+211
-22
lines changed

1 file changed

+211
-22
lines changed

sound/pci/hda/patch_hdmi.c

Lines changed: 211 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <linux/init.h>
2020
#include <linux/delay.h>
21+
#include <linux/pci.h>
2122
#include <linux/slab.h>
2223
#include <linux/module.h>
2324
#include <linux/pm_runtime.h>
@@ -119,6 +120,7 @@ struct hdmi_pcm {
119120
};
120121

121122
struct hdmi_spec {
123+
struct hda_codec *codec;
122124
int num_cvts;
123125
struct snd_array cvts; /* struct hdmi_spec_per_cvt */
124126
hda_nid_t cvt_nids[4]; /* only for haswell fix */
@@ -163,9 +165,11 @@ struct hdmi_spec {
163165
struct hda_multi_out multiout;
164166
struct hda_pcm_stream pcm_playback;
165167

166-
/* i915/powerwell (Haswell+/Valleyview+) specific */
167-
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
168+
bool use_jack_detect; /* jack detection enabled */
169+
bool use_acomp_notifier; /* use eld_notify callback for hotplug */
170+
bool acomp_registered; /* audio component registered in this driver */
168171
struct drm_audio_component_audio_ops drm_audio_ops;
172+
int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
169173

170174
struct hdac_chmap chmap;
171175
hda_nid_t vendor_nid;
@@ -765,6 +769,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
765769
static void jack_callback(struct hda_codec *codec,
766770
struct hda_jack_callback *jack)
767771
{
772+
/* stop polling when notification is enabled */
773+
if (codec_has_acomp(codec))
774+
return;
775+
768776
/* hda_jack don't support DP MST */
769777
check_presence_and_report(codec, jack->nid, 0);
770778
}
@@ -823,6 +831,9 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
823831
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
824832
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
825833

834+
if (codec_has_acomp(codec))
835+
return;
836+
826837
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
827838
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
828839
return;
@@ -1636,18 +1647,13 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
16361647
snd_hda_power_down_pm(codec);
16371648
return false;
16381649
}
1639-
}
1640-
1641-
if (codec_has_acomp(codec)) {
1650+
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
1651+
snd_hda_power_down_pm(codec);
1652+
} else {
16421653
sync_eld_via_acomp(codec, per_pin);
16431654
ret = false; /* don't call snd_hda_jack_report_sync() */
1644-
} else {
1645-
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
16461655
}
16471656

1648-
if (!codec_has_acomp(codec))
1649-
snd_hda_power_down_pm(codec);
1650-
16511657
return ret;
16521658
}
16531659

@@ -2252,18 +2258,24 @@ static int generic_hdmi_init(struct hda_codec *codec)
22522258
struct hdmi_spec *spec = codec->spec;
22532259
int pin_idx;
22542260

2261+
mutex_lock(&spec->pcm_lock);
2262+
spec->use_jack_detect = !codec->jackpoll_interval;
22552263
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
22562264
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
22572265
hda_nid_t pin_nid = per_pin->pin_nid;
22582266
int dev_id = per_pin->dev_id;
22592267

22602268
snd_hda_set_dev_select(codec, pin_nid, dev_id);
22612269
hdmi_init_pin(codec, pin_nid);
2262-
if (!codec_has_acomp(codec))
2270+
if (codec_has_acomp(codec))
2271+
continue;
2272+
if (spec->use_jack_detect)
2273+
snd_hda_jack_detect_enable(codec, pin_nid);
2274+
else
22632275
snd_hda_jack_detect_enable_callback(codec, pin_nid,
2264-
codec->jackpoll_interval > 0 ?
2265-
jack_callback : NULL);
2276+
jack_callback);
22662277
}
2278+
mutex_unlock(&spec->pcm_lock);
22672279
return 0;
22682280
}
22692281

@@ -2296,7 +2308,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
22962308
struct hdmi_spec *spec = codec->spec;
22972309
int pin_idx, pcm_idx;
22982310

2299-
if (codec_has_acomp(codec)) {
2311+
if (spec->acomp_registered) {
2312+
snd_hdac_acomp_exit(&codec->bus->core);
2313+
} else if (codec_has_acomp(codec)) {
23002314
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
23012315
codec->relaxed_resume = 0;
23022316
}
@@ -2364,6 +2378,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
23642378
if (!spec)
23652379
return -ENOMEM;
23662380

2381+
spec->codec = codec;
23672382
spec->ops = generic_standard_hdmi_ops;
23682383
spec->dev_num = 1; /* initialize to 1 */
23692384
mutex_init(&spec->pcm_lock);
@@ -2401,6 +2416,136 @@ static int patch_generic_hdmi(struct hda_codec *codec)
24012416
return 0;
24022417
}
24032418

2419+
/*
2420+
* generic audio component binding
2421+
*/
2422+
2423+
/* turn on / off the unsol event jack detection dynamically */
2424+
static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
2425+
bool use_acomp)
2426+
{
2427+
struct hda_jack_tbl *tbl;
2428+
2429+
tbl = snd_hda_jack_tbl_get(codec, nid);
2430+
if (tbl) {
2431+
/* clear unsol even if component notifier is used, or re-enable
2432+
* if notifier is cleared
2433+
*/
2434+
unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
2435+
snd_hda_codec_write_cache(codec, nid, 0,
2436+
AC_VERB_SET_UNSOLICITED_ENABLE, val);
2437+
} else {
2438+
/* if no jack entry was defined beforehand, create a new one
2439+
* at need (i.e. only when notifier is cleared)
2440+
*/
2441+
if (!use_acomp)
2442+
snd_hda_jack_detect_enable(codec, nid);
2443+
}
2444+
}
2445+
2446+
/* set up / clear component notifier dynamically */
2447+
static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
2448+
bool use_acomp)
2449+
{
2450+
struct hdmi_spec *spec;
2451+
int i;
2452+
2453+
spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
2454+
mutex_lock(&spec->pcm_lock);
2455+
spec->use_acomp_notifier = use_acomp;
2456+
spec->codec->relaxed_resume = use_acomp;
2457+
/* reprogram each jack detection logic depending on the notifier */
2458+
if (spec->use_jack_detect) {
2459+
for (i = 0; i < spec->num_pins; i++)
2460+
reprogram_jack_detect(spec->codec,
2461+
get_pin(spec, i)->pin_nid,
2462+
use_acomp);
2463+
}
2464+
mutex_unlock(&spec->pcm_lock);
2465+
}
2466+
2467+
/* enable / disable the notifier via master bind / unbind */
2468+
static int generic_acomp_master_bind(struct device *dev,
2469+
struct drm_audio_component *acomp)
2470+
{
2471+
generic_acomp_notifier_set(acomp, true);
2472+
return 0;
2473+
}
2474+
2475+
static void generic_acomp_master_unbind(struct device *dev,
2476+
struct drm_audio_component *acomp)
2477+
{
2478+
generic_acomp_notifier_set(acomp, false);
2479+
}
2480+
2481+
/* check whether both HD-audio and DRM PCI devices belong to the same bus */
2482+
static int match_bound_vga(struct device *dev, int subtype, void *data)
2483+
{
2484+
struct hdac_bus *bus = data;
2485+
struct pci_dev *pci, *master;
2486+
2487+
if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
2488+
return 0;
2489+
master = to_pci_dev(bus->dev);
2490+
pci = to_pci_dev(dev);
2491+
return master->bus == pci->bus;
2492+
}
2493+
2494+
/* audio component notifier for AMD/Nvidia HDMI codecs */
2495+
static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
2496+
{
2497+
struct hda_codec *codec = audio_ptr;
2498+
struct hdmi_spec *spec = codec->spec;
2499+
hda_nid_t pin_nid = spec->port2pin(codec, port);
2500+
2501+
if (!pin_nid)
2502+
return;
2503+
if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
2504+
return;
2505+
/* skip notification during system suspend (but not in runtime PM);
2506+
* the state will be updated at resume
2507+
*/
2508+
if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
2509+
return;
2510+
/* ditto during suspend/resume process itself */
2511+
if (snd_hdac_is_in_pm(&codec->core))
2512+
return;
2513+
2514+
check_presence_and_report(codec, pin_nid, dev_id);
2515+
}
2516+
2517+
/* set up the private drm_audio_ops from the template */
2518+
static void setup_drm_audio_ops(struct hda_codec *codec,
2519+
const struct drm_audio_component_audio_ops *ops)
2520+
{
2521+
struct hdmi_spec *spec = codec->spec;
2522+
2523+
spec->drm_audio_ops.audio_ptr = codec;
2524+
/* intel_audio_codec_enable() or intel_audio_codec_disable()
2525+
* will call pin_eld_notify with using audio_ptr pointer
2526+
* We need make sure audio_ptr is really setup
2527+
*/
2528+
wmb();
2529+
spec->drm_audio_ops.pin2port = ops->pin2port;
2530+
spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
2531+
spec->drm_audio_ops.master_bind = ops->master_bind;
2532+
spec->drm_audio_ops.master_unbind = ops->master_unbind;
2533+
}
2534+
2535+
/* initialize the generic HDMI audio component */
2536+
static void generic_acomp_init(struct hda_codec *codec,
2537+
const struct drm_audio_component_audio_ops *ops,
2538+
int (*port2pin)(struct hda_codec *, int))
2539+
{
2540+
struct hdmi_spec *spec = codec->spec;
2541+
2542+
spec->port2pin = port2pin;
2543+
setup_drm_audio_ops(codec, ops);
2544+
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
2545+
match_bound_vga, 0))
2546+
spec->acomp_registered = true;
2547+
}
2548+
24042549
/*
24052550
* Intel codec parsers and helpers
24062551
*/
@@ -2569,20 +2714,19 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
25692714
check_presence_and_report(codec, pin_nid, dev_id);
25702715
}
25712716

2717+
static const struct drm_audio_component_audio_ops intel_audio_ops = {
2718+
.pin2port = intel_pin2port,
2719+
.pin_eld_notify = intel_pin_eld_notify,
2720+
};
2721+
25722722
/* register i915 component pin_eld_notify callback */
25732723
static void register_i915_notifier(struct hda_codec *codec)
25742724
{
25752725
struct hdmi_spec *spec = codec->spec;
25762726

25772727
spec->use_acomp_notifier = true;
2578-
spec->drm_audio_ops.audio_ptr = codec;
2579-
/* intel_audio_codec_enable() or intel_audio_codec_disable()
2580-
* will call pin_eld_notify with using audio_ptr pointer
2581-
* We need make sure audio_ptr is really setup
2582-
*/
2583-
wmb();
2584-
spec->drm_audio_ops.pin2port = intel_pin2port;
2585-
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
2728+
spec->port2pin = intel_port2pin;
2729+
setup_drm_audio_ops(codec, &intel_audio_ops);
25862730
snd_hdac_acomp_register_notifier(&codec->bus->core,
25872731
&spec->drm_audio_ops);
25882732
/* no need for forcible resume for jack check thanks to notifier */
@@ -2980,6 +3124,7 @@ static int patch_simple_hdmi(struct hda_codec *codec,
29803124
if (!spec)
29813125
return -ENOMEM;
29823126

3127+
spec->codec = codec;
29833128
codec->spec = spec;
29843129
hdmi_array_init(spec, 1);
29853130

@@ -3284,6 +3429,26 @@ static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
32843429
return 0;
32853430
}
32863431

3432+
/* map from pin NID to port; port is 0-based */
3433+
/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
3434+
static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
3435+
{
3436+
return pin_nid - 4;
3437+
}
3438+
3439+
/* reverse-map from port to pin NID: see above */
3440+
static int nvhdmi_port2pin(struct hda_codec *codec, int port)
3441+
{
3442+
return port + 4;
3443+
}
3444+
3445+
static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
3446+
.pin2port = nvhdmi_pin2port,
3447+
.pin_eld_notify = generic_acomp_pin_eld_notify,
3448+
.master_bind = generic_acomp_master_bind,
3449+
.master_unbind = generic_acomp_master_unbind,
3450+
};
3451+
32873452
static int patch_nvhdmi(struct hda_codec *codec)
32883453
{
32893454
struct hdmi_spec *spec;
@@ -3300,6 +3465,8 @@ static int patch_nvhdmi(struct hda_codec *codec)
33003465
nvhdmi_chmap_cea_alloc_validate_get_type;
33013466
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
33023467

3468+
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
3469+
33033470
return 0;
33043471
}
33053472

@@ -3787,6 +3954,26 @@ static int atihdmi_init(struct hda_codec *codec)
37873954
return 0;
37883955
}
37893956

3957+
/* map from pin NID to port; port is 0-based */
3958+
/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
3959+
static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
3960+
{
3961+
return pin_nid / 2 - 1;
3962+
}
3963+
3964+
/* reverse-map from port to pin NID: see above */
3965+
static int atihdmi_port2pin(struct hda_codec *codec, int port)
3966+
{
3967+
return port * 2 + 3;
3968+
}
3969+
3970+
static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
3971+
.pin2port = atihdmi_pin2port,
3972+
.pin_eld_notify = generic_acomp_pin_eld_notify,
3973+
.master_bind = generic_acomp_master_bind,
3974+
.master_unbind = generic_acomp_master_unbind,
3975+
};
3976+
37903977
static int patch_atihdmi(struct hda_codec *codec)
37913978
{
37923979
struct hdmi_spec *spec;
@@ -3835,6 +4022,8 @@ static int patch_atihdmi(struct hda_codec *codec)
38354022
*/
38364023
codec->link_down_at_suspend = 1;
38374024

4025+
generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
4026+
38384027
return 0;
38394028
}
38404029

0 commit comments

Comments
 (0)