From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- sound/pci/hda/hda_jack.c | 770 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 770 insertions(+) create mode 100644 sound/pci/hda/hda_jack.c (limited to 'sound/pci/hda/hda_jack.c') diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c new file mode 100644 index 000000000..7d7786df6 --- /dev/null +++ b/sound/pci/hda/hda_jack.c @@ -0,0 +1,770 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Jack-detection handling for HD-audio + * + * Copyright (c) 2011 Takashi Iwai + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" + +/** + * is_jack_detectable - Check whether the given pin is jack-detectable + * @codec: the HDA codec + * @nid: pin NID + * + * Check whether the given pin is capable to report the jack detection. + * The jack detection might not work by various reasons, e.g. the jack + * detection is prohibited in the codec level, the pin config has + * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc. + */ +bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) +{ + if (codec->no_jack_detect) + return false; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) + return false; + if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE) + return false; + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && + !codec->jackpoll_interval) + return false; + return true; +} +EXPORT_SYMBOL_GPL(is_jack_detectable); + +/* execute pin sense measurement */ +static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ + u32 pincap; + u32 val; + + if (!codec->no_trigger_sense) { + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_SENSE, 0); + } + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, dev_id); + if (codec->inv_jack_detect) + val ^= AC_PINSENSE_PRESENCE; + return val; +} + +/** + * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to refer to + * @dev_id: pin device entry id + */ +struct hda_jack_tbl * +snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!nid || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid == nid && jack->dev_id == dev_id) + return jack; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst); + +/** + * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag + * @codec: the HDA codec + * @tag: tag value to refer to + * @dev_id: pin device entry id + */ +struct hda_jack_tbl * +snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, + unsigned char tag, int dev_id) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!tag || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->tag == tag && jack->dev_id == dev_id) + return jack; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag); + +static struct hda_jack_tbl * +any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!nid || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid == nid) + return jack; + return NULL; +} + +/** + * snd_hda_jack_tbl_new - create a jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to assign + * @dev_id: pin device entry id + */ +static struct hda_jack_tbl * +snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ + struct hda_jack_tbl *jack = + snd_hda_jack_tbl_get_mst(codec, nid, dev_id); + struct hda_jack_tbl *existing_nid_jack = + any_jack_tbl_get_from_nid(codec, nid); + + WARN_ON(dev_id != 0 && !codec->dp_mst); + + if (jack) + return jack; + jack = snd_array_new(&codec->jacktbl); + if (!jack) + return NULL; + jack->nid = nid; + jack->dev_id = dev_id; + jack->jack_dirty = 1; + if (existing_nid_jack) { + jack->tag = existing_nid_jack->tag; + + /* + * Copy jack_detect from existing_nid_jack to avoid + * snd_hda_jack_detect_enable_callback_mst() making multiple + * SET_UNSOLICITED_ENABLE calls on the same pin. + */ + jack->jack_detect = existing_nid_jack->jack_detect; + } else { + jack->tag = codec->jacktbl.used; + } + + return jack; +} + +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (!codec->bus->shutdown && jack->jack) + snd_device_disconnect(codec->card, jack->jack); + } +} + +void snd_hda_jack_tbl_clear(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + struct hda_jack_callback *cb, *next; + + /* free jack instances manually when clearing/reconfiguring */ + if (!codec->bus->shutdown && jack->jack) + snd_device_free(codec->card, jack->jack); + + for (cb = jack->callback; cb; cb = next) { + next = cb->next; + kfree(cb); + } + } + snd_array_free(&codec->jacktbl); +} + +#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) + +/* update the cached value and notification flag if needed */ +static void jack_detect_update(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + if (!jack->jack_dirty) + return; + + if (jack->phantom_jack) + jack->pin_sense = AC_PINSENSE_PRESENCE; + else + jack->pin_sense = read_pin_sense(codec, jack->nid, + jack->dev_id); + + /* A gating jack indicates the jack is invalid if gating is unplugged */ + if (jack->gating_jack && + !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id)) + jack->pin_sense &= ~AC_PINSENSE_PRESENCE; + + jack->jack_dirty = 0; + + /* If a jack is gated by this one update it. */ + if (jack->gated_jack) { + struct hda_jack_tbl *gated = + snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, + jack->dev_id); + if (gated) { + gated->jack_dirty = 1; + jack_detect_update(codec, gated); + } + } +} + +/** + * snd_hda_jack_set_dirty_all - Mark all the cached as dirty + * @codec: the HDA codec + * + * This function sets the dirty flag to all entries of jack table. + * It's called from the resume path in hda_codec.c. + */ +void snd_hda_jack_set_dirty_all(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) + jack->jack_dirty = 1; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all); + +/** + * snd_hda_jack_pin_sense - execute pin sense measurement + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * @dev_id: pin device entry id + * + * Execute necessary pin sense measurement and return its Presence Detect, + * Impedance, ELD Valid etc. status bits. + */ +u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ + struct hda_jack_tbl *jack = + snd_hda_jack_tbl_get_mst(codec, nid, dev_id); + if (jack) { + jack_detect_update(codec, jack); + return jack->pin_sense; + } + return read_pin_sense(codec, nid, dev_id); +} +EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense); + +/** + * snd_hda_jack_detect_state_mst - query pin Presence Detect status + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * @dev_id: pin device entry id + * + * Query and return the pin's Presence Detect status, as either + * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. + */ +int snd_hda_jack_detect_state_mst(struct hda_codec *codec, + hda_nid_t nid, int dev_id) +{ + struct hda_jack_tbl *jack = + snd_hda_jack_tbl_get_mst(codec, nid, dev_id); + if (jack && jack->phantom_jack) + return HDA_JACK_PHANTOM; + else if (snd_hda_jack_pin_sense(codec, nid, dev_id) & + AC_PINSENSE_PRESENCE) + return HDA_JACK_PRESENT; + else + return HDA_JACK_NOT_PRESENT; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst); + +static struct hda_jack_callback * +find_callback_from_list(struct hda_jack_tbl *jack, + hda_jack_callback_fn func) +{ + struct hda_jack_callback *cb; + + if (!func) + return NULL; + + for (cb = jack->callback; cb; cb = cb->next) { + if (cb->func == func) + return cb; + } + + return NULL; +} + +/** + * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection + * @codec: the HDA codec + * @nid: pin NID to enable + * @func: callback function to register + * @dev_id: pin device entry id + * + * In the case of error, the return value will be a pointer embedded with + * errno. Check and handle the return value appropriately with standard + * macros such as @IS_ERR() and @PTR_ERR(). + */ +struct hda_jack_callback * +snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, + int dev_id, hda_jack_callback_fn func) +{ + struct hda_jack_tbl *jack; + struct hda_jack_callback *callback = NULL; + int err; + + jack = snd_hda_jack_tbl_new(codec, nid, dev_id); + if (!jack) + return ERR_PTR(-ENOMEM); + + callback = find_callback_from_list(jack, func); + + if (func && !callback) { + callback = kzalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) + return ERR_PTR(-ENOMEM); + callback->func = func; + callback->nid = jack->nid; + callback->dev_id = jack->dev_id; + callback->next = jack->callback; + jack->callback = callback; + } + + if (jack->jack_detect) + return callback; /* already registered */ + jack->jack_detect = 1; + if (codec->jackpoll_interval > 0) + return callback; /* No unsol if we're polling instead */ + err = snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | jack->tag); + if (err < 0) + return ERR_PTR(err); + return callback; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst); + +/** + * snd_hda_jack_detect_enable - Enable the jack detection on the given pin + * @codec: the HDA codec + * @nid: pin NID to enable jack detection + * @dev_id: pin device entry id + * + * Enable the jack detection with the default callback. Returns zero if + * successful or a negative error code. + */ +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, + int dev_id) +{ + return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec, + nid, + dev_id, + NULL)); +} +EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); + +/** + * snd_hda_jack_set_gating_jack - Set gating jack. + * @codec: the HDA codec + * @gated_nid: gated pin NID + * @gating_nid: gating pin NID + * + * Indicates the gated jack is only valid when the gating jack is plugged. + */ +int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_nid) +{ + struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0); + struct hda_jack_tbl *gating = + snd_hda_jack_tbl_new(codec, gating_nid, 0); + + WARN_ON(codec->dp_mst); + + if (!gated || !gating) + return -EINVAL; + + gated->gating_jack = gating_nid; + gating->gated_jack = gated_nid; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); + +/** + * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack. + * @codec: the HDA codec + * @key_nid: key event is generated by this pin NID + * @keymap: map of key type and key code + * @jack_nid: key reports to the jack of this pin NID + * + * This function is used in the case of key is generated from one NID while is + * reported to the jack of another NID. + */ +int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid, + const struct hda_jack_keymap *keymap, + hda_nid_t jack_nid) +{ + const struct hda_jack_keymap *map; + struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid); + struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid); + + WARN_ON(codec->dp_mst); + + if (!key_gen || !report_to || !report_to->jack) + return -EINVAL; + + key_gen->key_report_jack = jack_nid; + + if (keymap) + for (map = keymap; map->type; map++) + snd_jack_set_key(report_to->jack, map->type, map->key); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap); + +/** + * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state. + * @codec: the HDA codec + * @jack_nid: the button event reports to the jack_tbl of this NID + * @button_state: the button event captured by codec + * + * Codec driver calls this function to report the button event. + */ +void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid, + int button_state) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid); + + if (!jack) + return; + + if (jack->key_report_jack) { + struct hda_jack_tbl *report_to = + snd_hda_jack_tbl_get(codec, jack->key_report_jack); + + if (report_to) { + report_to->button_state = button_state; + return; + } + } + + jack->button_state = button_state; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state); + +/** + * snd_hda_jack_report_sync - sync the states of all jacks and report if changed + * @codec: the HDA codec + */ +void snd_hda_jack_report_sync(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack; + int i, state; + + /* update all jacks at first */ + jack = codec->jacktbl.list; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) + jack_detect_update(codec, jack); + + /* report the updated jacks; it's done after updating all jacks + * to make sure that all gating jacks properly have been set + */ + jack = codec->jacktbl.list; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) { + if (!jack->jack || jack->block_report) + continue; + state = jack->button_state; + if (get_jack_plug_state(jack->pin_sense)) + state |= jack->type; + snd_jack_report(jack->jack, state); + if (jack->button_state) { + snd_jack_report(jack->jack, + state & ~jack->button_state); + jack->button_state = 0; /* button released */ + } + } +} +EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync); + +/* guess the jack type from the pin-config */ +static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + return SND_JACK_LINEOUT; + case AC_JACK_HP_OUT: + return SND_JACK_HEADPHONE; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return SND_JACK_AVOUT; + case AC_JACK_MIC_IN: + return SND_JACK_MICROPHONE; + default: + return SND_JACK_LINEIN; + } +} + +static void hda_free_jack_priv(struct snd_jack *jack) +{ + struct hda_jack_tbl *jacks = jack->private_data; + jacks->nid = 0; + jacks->jack = NULL; +} + +/** + * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID to assign + * @dev_id : pin device entry id + * @name: string name for the jack + * @phantom_jack: flag to deal as a phantom jack + * @type: jack type bits to be reported, 0 for guessing from pincfg + * @keymap: optional jack / key mapping + * + * This assigns a jack-detection kctl to the given pin. The kcontrol + * will have the given name and index. + */ +int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid, + int dev_id, const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap) +{ + struct hda_jack_tbl *jack; + const struct hda_jack_keymap *map; + int err, state, buttons; + + jack = snd_hda_jack_tbl_new(codec, nid, dev_id); + if (!jack) + return 0; + if (jack->jack) + return 0; /* already created */ + + if (!type) + type = get_input_jack_type(codec, nid); + + buttons = 0; + if (keymap) { + for (map = keymap; map->type; map++) + buttons |= map->type; + } + + err = snd_jack_new(codec->card, name, type | buttons, + &jack->jack, true, phantom_jack); + if (err < 0) + return err; + + jack->phantom_jack = !!phantom_jack; + jack->type = type; + jack->button_state = 0; + jack->jack->private_data = jack; + jack->jack->private_free = hda_free_jack_priv; + if (keymap) { + for (map = keymap; map->type; map++) + snd_jack_set_key(jack->jack, map->type, map->key); + } + + state = snd_hda_jack_detect_mst(codec, nid, dev_id); + snd_jack_report(jack->jack, state ? jack->type : 0); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst); + +static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + const char *base_name) +{ + unsigned int def_conf, conn; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int err; + bool phantom_jack; + + WARN_ON(codec->dp_mst); + + if (!nid) + return 0; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + return 0; + phantom_jack = (conn != AC_JACK_PORT_COMPLEX) || + !is_jack_detectable(codec, nid); + + if (base_name) + strscpy(name, base_name, sizeof(name)); + else + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL); + if (phantom_jack) + /* Example final name: "Internal Mic Phantom Jack" */ + strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); + err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); + if (err < 0) + return err; + + if (!phantom_jack) + return snd_hda_jack_detect_enable(codec, nid, 0); + return 0; +} + +/** + * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg + * @codec: the HDA codec + * @cfg: pin config table to parse + */ +int snd_hda_jack_add_kctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + const hda_nid_t *p; + int i, err; + + for (i = 0; i < cfg->num_inputs; i++) { + /* If we have headphone mics; make sure they get the right name + before grabbed by output pins */ + if (cfg->inputs[i].is_headphone_mic) { + if (auto_cfg_hp_outs(cfg) == 1) + err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0], + cfg, "Headphone Mic"); + else + err = add_jack_kctl(codec, cfg->inputs[i].pin, + cfg, "Headphone Mic"); + } else + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, + NULL); + if (err < 0) + return err; + } + + for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { + err = add_jack_kctl(codec, *p, cfg, NULL); + if (err < 0) + return err; + } + for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { + if (*p == *cfg->line_out_pins) /* might be duplicated */ + break; + err = add_jack_kctl(codec, *p, cfg, NULL); + if (err < 0) + return err; + } + for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { + if (*p == *cfg->line_out_pins) /* might be duplicated */ + break; + err = add_jack_kctl(codec, *p, cfg, NULL); + if (err < 0) + return err; + } + for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { + err = add_jack_kctl(codec, *p, cfg, NULL); + if (err < 0) + return err; + } + err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL); + if (err < 0) + return err; + err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL); + if (err < 0) + return err; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls); + +static void call_jack_callback(struct hda_codec *codec, unsigned int res, + struct hda_jack_tbl *jack) +{ + struct hda_jack_callback *cb; + + for (cb = jack->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; + cb->func(codec, cb); + } + if (jack->gated_jack) { + struct hda_jack_tbl *gated = + snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, + jack->dev_id); + if (gated) { + for (cb = gated->callback; cb; cb = cb->next) { + cb->jack = gated; + cb->unsol_res = res; + cb->func(codec, cb); + } + } + } +} + +/** + * snd_hda_jack_unsol_event - Handle an unsolicited event + * @codec: the HDA codec + * @res: the unsolicited event data + */ +void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct hda_jack_tbl *event; + int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT; + + if (codec->dp_mst) { + int dev_entry = + (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; + + event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); + } else { + event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); + } + if (!event) + return; + + if (event->key_report_jack) { + struct hda_jack_tbl *report_to = + snd_hda_jack_tbl_get_mst(codec, event->key_report_jack, + event->dev_id); + if (report_to) + report_to->jack_dirty = 1; + } else + event->jack_dirty = 1; + + call_jack_callback(codec, res, event); + snd_hda_jack_report_sync(codec); +} +EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); + +/** + * snd_hda_jack_poll_all - Poll all jacks + * @codec: the HDA codec + * + * Poll all detectable jacks with dirty flag, update the status, call + * callbacks and call snd_hda_jack_report_sync() if any changes are found. + */ +void snd_hda_jack_poll_all(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i, changes = 0; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + unsigned int old_sense; + if (!jack->nid || !jack->jack_dirty || jack->phantom_jack) + continue; + old_sense = get_jack_plug_state(jack->pin_sense); + jack_detect_update(codec, jack); + if (old_sense == get_jack_plug_state(jack->pin_sense)) + continue; + changes = 1; + call_jack_callback(codec, 0, jack); + } + if (changes) + snd_hda_jack_report_sync(codec); +} +EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all); + -- cgit v1.2.3