From c51070b7a86d4c756c7cb453bfabf6cb9cd5bcc8 Mon Sep 17 00:00:00 2001 From: Philip Peterson <1326208+philip-peterson@users.noreply.github.com> Date: Sat, 26 Sep 2026 01:25:14 -0800 Subject: [PATCH] Driver --- Containerfile | 52 +- README.md | 38 +- RUNME_PERIODICALLY.sh | 0 Taskfile.yml | 77 + docs/imac19-2-mic-fix.md | 235 + .../system/cs8409-intmike-power-fixup.service | 16 + .../quine-linux/cs8409-intmike-power-fixup.sh | 30 + ...c19-2-force-intmike-adc-nid-fallback.patch | 22 + patches/snd-hda-codec-cs8409/PROVENANCE.md | 60 + .../snd-hda-codec-cs8409/vendor/.gitignore | 8 + patches/snd-hda-codec-cs8409/vendor/Makefile | 29 + patches/snd-hda-codec-cs8409/vendor/README.md | 92 + .../vendor/hda_auto_parser.h | 118 + .../snd-hda-codec-cs8409/vendor/hda_generic.h | 366 + .../snd-hda-codec-cs8409/vendor/hda_jack.h | 195 + .../snd-hda-codec-cs8409/vendor/hda_local.h | 763 +++ .../vendor/patch_cirrus_apple.h | 3085 +++++++++ .../vendor/patch_cirrus_boot84.h | 2452 +++++++ .../vendor/patch_cirrus_hda_generic_copy.h | 432 ++ .../vendor/patch_cirrus_new84.h | 1906 ++++++ .../vendor/patch_cirrus_real84.h | 5954 +++++++++++++++++ .../vendor/patch_cirrus_real84_i2c.h | 1955 ++++++ .../vendor/patch_cs8409-tables.c | 623 ++ .../vendor/patch_cs8409.c | 1595 +++++ .../vendor/patch_cs8409.h | 540 ++ 25 files changed, 20640 insertions(+), 3 deletions(-) mode change 100644 => 100755 RUNME_PERIODICALLY.sh create mode 100644 Taskfile.yml create mode 100644 docs/imac19-2-mic-fix.md create mode 100644 overlay/etc/systemd/system/cs8409-intmike-power-fixup.service create mode 100755 overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh create mode 100644 patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch create mode 100644 patches/snd-hda-codec-cs8409/PROVENANCE.md create mode 100644 patches/snd-hda-codec-cs8409/vendor/.gitignore create mode 100644 patches/snd-hda-codec-cs8409/vendor/Makefile create mode 100644 patches/snd-hda-codec-cs8409/vendor/README.md create mode 100644 patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/hda_generic.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/hda_jack.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/hda_local.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c create mode 100644 patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h diff --git a/Containerfile b/Containerfile index bfc9b0b..2c893f1 100644 --- a/Containerfile +++ b/Containerfile @@ -1,7 +1,55 @@ -FROM ghcr.io/ublue-os/aurora:stable +ARG BASE_DIGEST=sha256:fbcef6075541eec7c2ca4a91c87c7012455cfba4e5e748d0cf2013d6aa3e66d3 + +# --- Builds the out-of-tree snd-hda-codec-cs8409 module (iMac19,2 mic fix). --- +# See docs/imac19-2-mic-fix.md and patches/snd-hda-codec-cs8409/ for the full +# story. Kept as its own stage so the build toolchain never lands in the +# final image, and pinned to the same BASE_DIGEST as the final stage so the +# kernel-devel headers installed here can never drift out of sync with the +# kernel that actually ships below. +FROM ghcr.io/ublue-os/aurora@${BASE_DIGEST} AS kmod-builder + +RUN set -eux; \ + NVR="$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')"; \ + VERSION="$(rpm -q kernel-core --qf '%{VERSION}')"; \ + RELEASE="$(rpm -q kernel-core --qf '%{RELEASE}')"; \ + echo "Building snd-hda-codec-cs8409 against kernel ${NVR}"; \ + dnf install -y gcc make elfutils-libelf-devel bc perl patch; \ + dnf install -y "kernel-devel-${NVR}" \ + || dnf install -y "https://kojipkgs.fedoraproject.org/packages/kernel/${VERSION}/${RELEASE}/x86_64/kernel-devel-${NVR}.rpm"; \ + echo "${NVR}" > /kernel-nvr.txt + +COPY patches/snd-hda-codec-cs8409/vendor/ /usr/src/snd-hda-codec-cs8409/ +COPY patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch /tmp/ + +# --fuzz=0: fail loudly on a context mismatch (e.g. a future kernel bump that +# shifts this file) instead of silently mis-patching. +RUN set -eux; \ + cd /usr/src/snd-hda-codec-cs8409; \ + patch -p1 --fuzz=0 < /tmp/0001-imac19-2-force-intmike-adc-nid-fallback.patch; \ + make KVER="$(cat /kernel-nvr.txt)"; \ + mkdir -p /output; \ + cp snd-hda-codec-cs8409.ko /output/ + +# --- Final image. --- +FROM ghcr.io/ublue-os/aurora@${BASE_DIGEST} AS final COPY overlay/ / +# alsa-tools provides hda-verb, used by cs8409-intmike-power-fixup.service. +RUN dnf install -y alsa-tools + +COPY --from=kmod-builder /output/snd-hda-codec-cs8409.ko /tmp/snd-hda-codec-cs8409.ko + +# Installs into .../extra/, which kmod's default search order gives +# precedence over the in-tree copy under .../kernel/... — this is the same +# mechanism Fedora's own kmod-* subpackages use to override an in-tree +# module of the same name/modalias. No blacklisting or renaming needed. +RUN set -eux; \ + NVR="$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')"; \ + mkdir -p "/usr/lib/modules/${NVR}/extra"; \ + mv /tmp/snd-hda-codec-cs8409.ko "/usr/lib/modules/${NVR}/extra/snd-hda-codec-cs8409.ko"; \ + depmod -a "${NVR}" + # System-level units are enabled explicitly. User units are picked up # automatically via overlay/usr/lib/systemd/user-preset/. -RUN systemctl enable rpm-ostree-cleanup.timer +RUN systemctl enable rpm-ostree-cleanup.timer cs8409-intmike-power-fixup.service diff --git a/README.md b/README.md index 6be2cda..6dc97b2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,27 @@ Quine distro of Linux based on uBlue Aurora ## Building `Containerfile` builds a custom bootc image on top of `ghcr.io/ublue-os/aurora:stable`, -layering in everything under `overlay/`: +layering in everything under `overlay/`, plus a `kmod-builder` stage that +compiles the CS8409 mic-fix kernel module (see +[Hardware-specific patches](#hardware-specific-patches) below). + +[`Taskfile.yml`](Taskfile.yml) (requires [`task`](https://taskfile.dev)) is +the supported way to drive all of this — run `task` with no arguments to +list what's available. Notably: + +- `task build-image` — builds the image locally (`podman build`, as root). +- `task build-iso` — builds a bootable install ISO from that image via + `bootc-image-builder`, landing in `output/bootiso/install.iso`. +- `task test:stage` / `task test:boot` — stage the locally-built image as + this machine's next boot target (`bootc switch`), optionally rebooting + into it immediately. The fast way to test a change — especially the + kernel module — against the real running system, without building an ISO + or reinstalling anything. +- `task revert` — rolls back to the previously-booted deployment and + reboots, undoing a `test:boot` switch (or any other deployment change) if + something's wrong. + +Equivalently, by hand: ```sh podman build -t quine-linux -f Containerfile . @@ -46,3 +66,19 @@ Weekly, runs as the user, no privilege needed: `podman system prune -af`, `flatpak uninstall --unused -y`, clears `~/.cache`. Auto-enabled for every user via `overlay/usr/lib/systemd/user-preset/90-quine-linux.preset` — no manual `systemctl --user enable` needed. + +### overlay/etc/systemd/system/cs8409-intmike-power-fixup.service + +Hardware-specific fixup for the iMac19,2 internal mic (see below). + +## Hardware-specific patches + +`docs/` and `patches/` hold write-ups and out-of-tree kernel module patches +for hardware quirks on the machines this image runs on, that don't fit +anywhere else in this repo: + +- [`docs/imac19-2-mic-fix.md`](docs/imac19-2-mic-fix.md) — the internal mic + on iMac19,2 capturing near-silence despite maxed gain; root cause, fix, + and current verification status. +- `patches/snd-hda-codec-cs8409/` — the vendored driver source and patch + backing that fix, built into `Containerfile`'s `kmod-builder` stage. diff --git a/RUNME_PERIODICALLY.sh b/RUNME_PERIODICALLY.sh old mode 100644 new mode 100755 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..af30705 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,77 @@ +version: '3' + +vars: + IMAGE: quine-linux + OUTPUT_DIR: ./output + +tasks: + default: + desc: List available tasks + cmds: + - task --list + silent: true + + build-image: + desc: >- + Build the quine-linux bootc container image, as root (so bootc and + bootc-image-builder -- both of which read root's default + containers-storage location -- can see the result). + cmds: + - sudo podman build -t {{.IMAGE}} -f Containerfile . + sources: + - Containerfile + - overlay/**/* + - patches/**/* + + build-iso: + desc: >- + Build a bootable install ISO from the local quine-linux image, via + bootc-image-builder. Output lands in {{.OUTPUT_DIR}}/bootiso/install.iso. + deps: [build-image] + cmds: + - mkdir -p {{.OUTPUT_DIR}} + - >- + sudo podman run --rm --privileged --pull=newer + --security-opt label=type:unconfined_t + -v {{.OUTPUT_DIR}}:/output + -v /var/lib/containers/storage:/var/lib/containers/storage + quay.io/centos-bootc/bootc-image-builder:latest + --type iso --local {{.IMAGE}} + generates: + - "{{.OUTPUT_DIR}}/bootiso/install.iso" + + status: + desc: Show current bootc deployment status (what's booted, what's staged/rollback). + cmds: + - bootc status + + test:stage: + desc: >- + Build the local image and stage it as the next-boot target on THIS + machine (bootc switch, no reboot yet). This is the fast way to test a + change -- especially the CS8409 mic-fix kernel module -- against the + real running system without building an ISO or reinstalling anything. + Reboot separately when ready: `task test:boot`, or `sudo systemctl reboot`. + deps: [build-image] + cmds: + - sudo bootc switch --transport containers-storage localhost/{{.IMAGE}} + - bootc status --booted + - echo "Staged localhost/{{.IMAGE}} for next boot. Run task test:boot when ready to reboot into it." + + test:boot: + desc: Stage the local image (if not already) and reboot into it now. + prompt: This will reboot this machine into the locally-built {{.IMAGE}} image. Continue? + deps: [test:stage] + cmds: + - sudo systemctl reboot + + revert: + desc: >- + Roll back to the previously-booted deployment and reboot into it -- + undoes a `test:boot` switch (or any other deployment change) if + something's wrong. Safe even if nothing was ever switched: bootc/ + rpm-ostree always keep the prior deployment around for exactly this. + prompt: This will reboot this machine back into the previous deployment. Continue? + cmds: + - sudo bootc rollback + - sudo systemctl reboot diff --git a/docs/imac19-2-mic-fix.md b/docs/imac19-2-mic-fix.md new file mode 100644 index 0000000..a53d816 --- /dev/null +++ b/docs/imac19-2-mic-fix.md @@ -0,0 +1,235 @@ +# iMac19,2 internal mic: silent capture despite maxed gain + +## TL;DR + +The internal mic shows up normally in PipeWire/ALSA/Discord, with fully +adjustable gain controls, but captures almost nothing regardless of how high +any gain is pushed. Root cause: the CS8409 codec driver's HDA auto-config +binds the exposed capture PCM stream to the wrong ADC node, orphaning the +real internal-mic ADC entirely — its mixer controls work, but nothing is +ever routed through them. A fix for this exact bug, confirmed on a sibling +Mac model, is packaged into this repo's build (`patches/snd-hda-codec-cs8409/`) +but **not yet verified on this exact iMac19,2 hardware** — see +[Current verification status](#current-verification-status). + +## Hardware / software identification + +- Model: Apple iMac19,2 (2019, 21.5") +- Distro: Aurora (uBlue/Fedora Kinoite bootc image), Kinoite variant + `44.20260908.1` +- Kernel: `7.1.8-200.fc44.x86_64` +- Audio codec: Cirrus Logic CS8409, HDA vendor id `0x10138409`, subsystem id + `0x106b0f00` (confirmed via `/proc/asound/card0/codec#0`) +- Secure Boot: unsupported/disabled on this hardware (`mokutil --sb-state`) + — a documented non-goal of this fix; see [Open risks](#open-risks--unknowns). + +## Symptom + +Mic appears as a normal PipeWire source (`Built-in Audio Analog Stereo`) and +ALSA capture device (`CS8409 Analog`, card 0). All of the following were +confirmed at 100% (or higher) with no effect on captured level: + +- ALSA `Internal Mic` capture volume: 100% (63/63), unmuted +- ALSA `Internal Mic Boost`: 100% (2/2) +- PipeWire source software volume: tested at 1.0, 4.0, and 8.0 (up to +18dB) +- Discord's own in-app input-volume slider: maxed + +Direct measurement, bypassing Discord and any GUI entirely, via +`pw-record --target ` / `arecord`, analyzed with +`ffmpeg -af volumedetect`: + +| Gain setting | mean_volume | max_volume | +|---|---|---| +| PipeWire source @ 1.0x (100%, all ALSA gain also maxed) | -80.8 dB | -71.2 dB | +| PipeWire source @ 4.0x | -59.7 dB | -50.3 dB | +| PipeWire source @ 8.0x | -59.7 dB | -50.3 dB (uncontrolled re-test, not a true ceiling — see note below) | + +The 1x → 4x jump is real, reproducible signal (matches the expected +amplification). The identical 4x/8x reading is *not* evidence of a hard +ceiling — it came from two takes that weren't spoken into with controlled, +identical volume/distance, so it's not a clean A/B. Even at its best +measured point (-50dB max), this is far too quiet for usable speech (normal +speech should peak around -12 to -6dB) — consistent with "gain is working, +but there's very little real signal to amplify in the first place." + +## Ruled out, with evidence + +- **Wrong/muted output device** — a real but separate issue found early on: + the *output* sink was muted at 0% (unrelated to the mic). Fixed + independently (`wpctl set-mute @DEFAULT_AUDIO_SINK@ 0`). +- **PipeWire/ALSA mute or wrong default device** — `wpctl status`, + `pactl list sources`, and `amixer` all independently agreed the mic source + was unmuted and at 100%+ from three different vantage points. +- **Discord-side gain/config** — Discord's own input-volume slider was + already at maximum; its `settings.json` doesn't store per-account + voice/mic settings in plaintext (those sync via Discord's own account + storage), so this was ruled out by the maxed-slider observation rather + than a config file. +- **The camera providing the audio instead of the codec** — `arecord -l` + shows exactly **one** ALSA capture device (`CS8409 Analog`, card 0); the + FaceTime HD Camera (USB `05ac:8511`) exposes no audio interface to Linux + at all (checked via `lsusb` + absence from `arecord -l`/`arecord -L`). +- **Apple T2 chip audio routing** — ruled out: no Apple PCI bridge/T2 device + present (`lspci -nn`), and iMac19,2 (2019 21.5") predates T2's presence in + Macs' internal-mic path regardless. +- **Missing Flatpak/portal permission for Discord's mic access** — + `wpctl status` showed Discord's own `WEBRTC VoiceEngine` client (verified + by cross-referencing the sandboxed PID against the host process list) with + an *active* capture stream connected to the real hardware node — a + permission denial would show no connected stream at all, not merely low + level. + +## Root cause + +Traced to a specific, very recent (2026-08-19) upstream bug report and fix +on a sibling Mac model: + +- **Issue**: [`egorenar/snd-hda-codec-cs8409` #5, "mic input"](https://github.com/egorenar/snd-hda-codec-cs8409/issues/5) +- **Diagnostic dead-end + panic warning + D3 discovery** (2026-08-17): + https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5314393975 +- **Root cause + fix, confirmed working** (2026-08-19): + https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5338459155 +- **Fork/PR carrying Linux 6.17+ HDA API compatibility**: + [PR #30](https://github.com/egorenar/snd-hda-codec-cs8409/pull/30), + `daviddoll/snd-hda-codec-cs8409@kernel-6.17-hda-compat`, open/unmerged. + +In `patch_cirrus_apple.h`, function `cs_8409_apple_boot_init()`, the +`SNDRV_PCM_STREAM_CAPTURE` branch only handles two cases for the capture +stream's `hinfo->nid`: + +- `hinfo->nid == spec->intmike_adc_nid` — the real internal-mic ADC, set up + correctly. +- `hinfo->nid == 0x1a` — the headset-mic node, set up correctly. + +There's no fallback for when HDA auto-config lands the exposed capture PCM +stream on **neither** — e.g. landing on node `0x12`, wired to an unconfigured +external Line-In pin (`0x34`) with no physical signal path at all. When this +happens, the real internal-mic ADC (`spec->intmike_adc_nid`, `0x23` for our +subsystem-id group) has all its mixer/gain/switch controls correctly present +and adjustable — which is exactly why raising every gain control had no +effect — but is never attached to any PCM stream, so it's completely +orphaned. Confirmed on the sibling model via `journalctl -kf` during a live +`arecord` test: the internal-mic-specific prepare hook was never called, +only the generic default path ran. + +Our subsystem id (`0x106b0f00`) is grouped together with the sibling model's +family (`0x106b1000`, `0x106b0f00`, `0x106b0e00`) in this same file's +`intmike_adc_nid` assignment logic — all three get `intmike_nid = 0x45`, +`intmike_adc_nid = 0x23` — matching our own kernel log +(`snd_hda_codec_cs8409 hdaudioC0D0: ... Internal Mic=0x45`). So this is a +plausible direct match, though **confirmed only on iMac18,1, not this exact +iMac19,2** — see [Open risks](#open-risks--unknowns). + +On the sibling model, applying the fix took capture from -91dB pure digital +silence to a real -16.9dB mean / -0.0dB max signal. + +## The fix + +See [`patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch`](../patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch) +for the actual diff (source of truth — not duplicated here). In short: if +the capture stream's `hinfo->nid` matches neither the internal-mic ADC nor +the headset-mic node, force it onto the internal-mic ADC before the existing +setup code runs. + +### A second, related issue (defensive, unconfirmed on this model) + +On the sibling model, the internal-mic ADC node also came up in HDA power +state D3 (suspended) after cold boot, failing capture with an I/O error +until forced to D0 via `hda-verb ... 0x23 SET_POWER_STATE 0x0` — not +persistent across reboots. This repo includes a defensive systemd unit +(`cs8409-intmike-power-fixup.service`, running +`overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh`) that logs +the power state and retries forcing D0 a few seconds after boot. **Whether +iMac19,2 actually hits this at all is unconfirmed** — its own journal output +after the real test boot is the evidence that will settle it. + +## Packaging approach + +The fix is built into this repo's own bootc image rather than hand-patched +live on the running system, specifically to avoid a documented panic risk +(see [Open risks](#open-risks--unknowns)) and to get free, cheap rollback via +`bootc rollback`/`rpm-ostree rollback` if anything goes wrong. + +- `patches/snd-hda-codec-cs8409/vendor/` — a verbatim copy of the 16 files + from the pinned upstream commit (`daviddoll/snd-hda-codec-cs8409` @ + `14e00de4290e090afd7632bc6d5c724131a19e7b`), vendored rather than fetched + at build time because this is an unmerged personal-fork branch that could + be force-pushed or deleted with no notice. See + [`PROVENANCE.md`](../patches/snd-hda-codec-cs8409/PROVENANCE.md) for the + full origin/license/rationale writeup (license: `GPL-2.0-or-later`, one + file `GPL-2.0-only`, per each file's own original SPDX header). +- `patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch` — + the actual functional change, kept separate from the vendored copy so it's + easy to re-diff if/when PR #30 lands upstream and this vendoring can be + dropped. +- `Containerfile` — a new `kmod-builder` stage installs kernel headers + matching the exact kernel shipped in the `aurora:stable` base image + (pinned by digest, shared between both stages via a single `ARG`, so they + can't drift apart), applies the patch, and builds the module with the + vendored `Makefile`. The final stage copies the resulting `.ko` into + `/usr/lib/modules//extra/` (the same convention Fedora's + own `kmod-*` subpackages use to override an in-tree module of the same + name/modalias — no blacklisting or renaming needed) and re-runs `depmod` + against the final image's own module tree. +- DKMS was deliberately **not** used: it exists to rebuild modules at + boot/install time when a kernel updates later on a mutable host. On this + bootc image, a new kernel only ever arrives bundled inside a freshly built + image, at which point the `kmod-builder` stage reruns from scratch anyway + — DKMS's rebuild-on-boot machinery is redundant weight here. + +## Current verification status + +**Not yet tested on real hardware as of 2026-09-26.** The patch has been +verified to apply cleanly (`git apply --check`) and produce the intended +diff against the vendored source. The Containerfile has been written and a +local `podman build` sanity check (module compiles, `vermagic` matches the +target kernel) is the next step. Actually switching this machine to boot the +built image (`bootc switch`/`rpm-ostree rebase` + reboot) is a separate, +explicitly confirmed step, not yet taken, given this hardware/kernel/fix +combination is entirely unverified. This section will be updated with real +results (measured dB, Discord input-meter behavior, and whether the D3 +power-state unit actually fired) once that test happens. + +## Open risks / unknowns + +- **Kernel API mismatch risk**: the vendored fork targets Linux 6.17+ HDA + API; our kernel is 7.1.8. Untested whether it compiles cleanly without + further porting — but this fails safely at `podman build` time, before + ever touching the running system. +- **Unconfirmed on this exact model**: the fix is confirmed only on + iMac18,1, not this iMac19,2/subsystem-id combination, even though the + `intmike_adc_nid` code path is shared across both per direct source + inspection. +- **D3 power-state issue unconfirmed on this model** — only confirmed on the + sibling; the defensive unit's journal is the intended way to settle this. +- **Module search-path precedence** (`extra/` overriding in-tree + `kernel/...`) is asserted from documented Fedora/kmod convention, not yet + verified on this image — check `modinfo -F filename + snd_hda_codec_cs8409` after the real test boot. +- **Live-unbind panic risk**: a live `unbind`/`rmmod`/`modprobe`/`bind` cycle + of this module while PipeWire has the sink active caused an unrecoverable + freeze on the sibling model. This is exactly why all testing here goes + through a full image build + reboot, never a live module reload. +- **Secure Boot non-goal**: this approach assumes Secure Boot stays + disabled on this hardware. If it's ever enabled, this entire approach + breaks (an unsigned out-of-tree `.ko` gets rejected at load) and would + need MOK enrollment + module signing added to the builder stage. +- **Personal-fork provenance risk**: the ultimate origin of the 6.17+ + compatibility work is an unmerged personal-fork PR (#30). Vendoring + mitigates the "it could disappear" risk but doesn't change that this + hasn't gone through upstream review. +- `quine-linux`'s own `rpm-ostree-cleanup.timer` prunes the rollback + deployment weekly — worth being aware of before testing an unverified + change on this hardware, though no action has been taken on this yet. + +## References + +- https://github.com/egorenar/snd-hda-codec-cs8409/issues/5 +- https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5314393975 +- https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5338459155 +- https://github.com/egorenar/snd-hda-codec-cs8409/pull/30 +- https://github.com/daviddoll/snd-hda-codec-cs8409/tree/kernel-6.17-hda-compat +- https://github.com/rojoxstar/imac19-2-cs8409-linux (iMac19,2-specific + project; primarily speaker/playback and Apple-DSP research, explicitly + does not claim mic-capture support — investigated and set aside in favor + of the fix above, which directly targets the observed symptom) diff --git a/overlay/etc/systemd/system/cs8409-intmike-power-fixup.service b/overlay/etc/systemd/system/cs8409-intmike-power-fixup.service new file mode 100644 index 0000000..a4448a0 --- /dev/null +++ b/overlay/etc/systemd/system/cs8409-intmike-power-fixup.service @@ -0,0 +1,16 @@ +[Unit] +Description=Force CS8409 internal-mic ADC out of D3 power state (iMac19,2 mic fix) +# See docs/imac19-2-mic-fix.md. Confirmed needed on a sibling model +# (iMac18,1); unconfirmed on iMac19,2 as of the date in that doc. This unit +# is defensive: it logs the power state it finds either way, so its journal +# is the evidence for whether this model actually needs it. +After=sound.target systemd-udev-settle.service +ConditionPathExists=/dev/snd/hwC0D0 + +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh + +[Install] +WantedBy=multi-user.target diff --git a/overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh b/overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh new file mode 100755 index 0000000..66e3f6e --- /dev/null +++ b/overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Forces the CS8409 internal-mic ADC (nid 0x23 on iMac19,2/18,x-family +# subsystem ids) out of D3 power state on boot. See +# docs/imac19-2-mic-fix.md in the quine-linux repo for the full story. +# +# Confirmed needed on a sibling model (iMac18,1); unconfirmed on iMac19,2. +# This logs the power state on every attempt, so the journal for this unit +# is the evidence for whether this model needs it at all. Always exits 0 -- +# this is a best-effort defensive fixup, not something that should fail boot. +set -u + +dev=/dev/snd/hwC0D0 +nid=0x23 + +log() { echo "cs8409-intmike-power-fixup: $*"; } + +log "initial state: $(hda-verb "$dev" "$nid" GET_POWER_STATE 0x0 2>&1 | tail -1)" + +for i in $(seq 1 10); do + hda-verb "$dev" "$nid" SET_POWER_STATE 0x0 >/dev/null 2>&1 + sleep 1 + state="$(hda-verb "$dev" "$nid" GET_POWER_STATE 0x0 2>&1 | tail -1)" + log "attempt $i state: $state" + case "$state" in + *0x0*) log "reached D0 after $i attempt(s)"; exit 0 ;; + esac +done + +log "gave up after 10 attempts, last state: $state" +exit 0 diff --git a/patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch b/patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch new file mode 100644 index 0000000..f275ab9 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch @@ -0,0 +1,22 @@ +--- a/patch_cirrus_apple.h ++++ b/patch_cirrus_apple.h +@@ -1257,6 +1257,19 @@ + } + else if (stream == SNDRV_PCM_STREAM_CAPTURE) + { ++ // iMac19,2 mic fix: HDA auto-config sometimes lands this ++ // capture PCM stream's nid on neither the internal-mic ADC ++ // nor the headset-mic node (e.g. it lands on an unwired ++ // Line-In node such as 0x12), orphaning the real internal ++ // mic entirely -- its mixer/gain controls stay present and ++ // adjustable, but nothing is ever routed through them. ++ // Force it back onto the internal-mic ADC in that case. ++ // See egorenar/snd-hda-codec-cs8409#5 (comment 2026-08-19). ++ if (hinfo->nid != spec->intmike_adc_nid && hinfo->nid != 0x1a) ++ { ++ mycodec_dbg(codec, "cs_8409_apple_boot_init capture stream nid 0x%02x is not intmike (0x%02x) or headset (0x1a), forcing to intmike\n", hinfo->nid, spec->intmike_adc_nid); ++ hinfo->nid = spec->intmike_adc_nid; ++ } + //if (hinfo->nid == 0x22) + if (hinfo->nid == spec->intmike_adc_nid) + { diff --git a/patches/snd-hda-codec-cs8409/PROVENANCE.md b/patches/snd-hda-codec-cs8409/PROVENANCE.md new file mode 100644 index 0000000..fda8d4e --- /dev/null +++ b/patches/snd-hda-codec-cs8409/PROVENANCE.md @@ -0,0 +1,60 @@ +# Provenance: vendored `snd-hda-codec-cs8409` source + +## Origin + +- Upstream project: [`egorenar/snd-hda-codec-cs8409`](https://github.com/egorenar/snd-hda-codec-cs8409) + (out-of-tree Linux HDA codec driver adding Apple Cirrus CS8409/CS42L42/CS42L83 + support beyond what's in mainline). +- Actual source vendored here comes from a fork/branch with Linux 6.17+ HDA + API compatibility work, not yet merged upstream: + [`daviddoll/snd-hda-codec-cs8409`](https://github.com/daviddoll/snd-hda-codec-cs8409), + branch `kernel-6.17-hda-compat`, submitted as + [PR #30](https://github.com/egorenar/snd-hda-codec-cs8409/pull/30) + ("Port out-of-tree CS8409 driver for Linux 6.17+ HDA API") — open, unmerged + as of the date below. +- **Pinned commit:** `14e00de4290e090afd7632bc6d5c724131a19e7b` + (2026-04-04, "Port for Linux 6.17+ HDA codec API and modpost fixes"). +- **Date vendored:** 2026-09-26. + +## Why vendored instead of fetched at build time + +This is an unmerged personal-fork branch, not a stable upstream release — +it can be force-pushed, renamed, or deleted with no notice. Vendoring a +verbatim copy here (original SPDX headers untouched) makes the quine-linux +build offline-capable, immune to that repo's fate, and keeps the +corresponding source actually present in this repo's own git history +alongside the binary it produces, which is the most direct way to satisfy +GPL-2.0-or-later's source-availability obligation. + +## What's in `vendor/` + +All 16 files from the pinned commit, unmodified: + +``` +Makefile, README.md, .gitignore, +hda_auto_parser.h, hda_generic.h, hda_jack.h, hda_local.h, +patch_cirrus_apple.h, patch_cirrus_boot84.h, patch_cirrus_hda_generic_copy.h, +patch_cirrus_new84.h, patch_cirrus_real84.h, patch_cirrus_real84_i2c.h, +patch_cs8409-tables.c, patch_cs8409.c, patch_cs8409.h +``` + +## License + +`GPL-2.0-or-later` (verified directly from the SPDX headers present in the +vendored files, e.g. `patch_cs8409.c`, `patch_cirrus_apple.h`, +`patch_cs8409.h`, `hda_generic.h`). One file, `patch_cs8409-tables.c`, carries +`GPL-2.0-only` specifically — the combined work is distributed here under +`GPL-2.0-or-later` for the files that specify it, and `GPL-2.0-only` for that +one file, matching each file's own original header. + +## Our changes + +The actual functional fix lives as a separate patch, applied on top of this +vendored tree at image-build time, not folded into the vendored copy: + +- `0001-imac19-2-force-intmike-adc-nid-fallback.patch` — see + `../../docs/imac19-2-mic-fix.md` for the full root-cause writeup. + +Keeping "what upstream wrote" (this directory) and "what we changed" (the +`.patch` file) separate makes it trivial to re-diff against a future upstream +merge of PR #30, and to drop this vendoring entirely once/if that lands. diff --git a/patches/snd-hda-codec-cs8409/vendor/.gitignore b/patches/snd-hda-codec-cs8409/vendor/.gitignore new file mode 100644 index 0000000..b2134d1 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/.gitignore @@ -0,0 +1,8 @@ +*.cmd +*.ko +*.o +Module.symvers +modules.order +*.mod +*.mod.c +.DS_Store diff --git a/patches/snd-hda-codec-cs8409/vendor/Makefile b/patches/snd-hda-codec-cs8409/vendor/Makefile new file mode 100644 index 0000000..e49c236 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/Makefile @@ -0,0 +1,29 @@ +# Build from this directory: make (or sudo make install) +# Do not run only "make -C /lib/modules/$(uname -r)/build M=..." — that skips +# CFLAGS_MODULE below and used to break the tree before patch_cs8409.h defaulted +# APPLE_CODECS. If you must call kbuild by hand, repeat the same CFLAGS_MODULE +# string as in the "all:" recipe. + +snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o +obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o + +# debug build flags +#KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_DEBUG=1 -DMYSOUNDDEBUGFULL -DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" +# normal build flags +KBUILD_EXTRA_CFLAGS = "-DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" + +ifdef KVER +KDIR := /lib/modules/$(KVER) +else +KDIR := /lib/modules/$(shell uname -r) +endif + +all: + make -C $(KDIR)/build CFLAGS_MODULE=$(KBUILD_EXTRA_CFLAGS) M=$(shell pwd) modules +clean: + make -C $(KDIR)/build M=$(shell pwd) clean + +install: + mkdir -p $(KDIR)/updates/ + cp snd-hda-codec-cs8409.ko $(KDIR)/updates/ + depmod -a diff --git a/patches/snd-hda-codec-cs8409/vendor/README.md b/patches/snd-hda-codec-cs8409/vendor/README.md new file mode 100644 index 0000000..112fbc4 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/README.md @@ -0,0 +1,92 @@ +# snd-hda-codec-cs8409 (out-of-tree) + +Out-of-tree Linux kernel module for the **Cirrus Logic CS8409** HD Audio bridge and related fixups (including **Apple** machine quirks). It mirrors the in-kernel ALSA HDA codec driver logic but can be built and updated without rebuilding the whole kernel. + +## Requirements + +- Linux kernel headers for the kernel you are running + (e.g. on Debian/Ubuntu/Pop!_OS: `linux-headers-$(uname -r)`) +- Toolchain able to build external modules (`gcc`, `make`, etc.) +- Build from a shell where `uname -r` matches the target kernel (or set `KVER` — see below) + +## Build + +Always build **from this directory** using the provided `Makefile` so module C flags are applied correctly: + +```bash +cd /path/to/snd-hda-codec-cs8409 +make +``` + +Do **not** run only: + +```text +make -C /lib/modules/$(uname -r)/build M=$(pwd) modules +``` + +without passing the same `CFLAGS_MODULE` values as in the `Makefile` `all:` recipe. The Makefile sets defines such as `APPLE_CODECS` and `CONFIG_SND_HDA_RECONFIG` that this tree expects. + +### Optional: build for another installed kernel + +```bash +make KVER=6.18.7-76061807-generic +``` + +(Use the directory name under `/lib/modules/` for that kernel.) + +### Install module into `updates/` (takes precedence over in-tree module) + +```bash +sudo make install +sudo depmod -a # install target runs depmod; repeat if you copy the .ko manually +``` + +Reload the audio stack or reboot as appropriate on your distribution (e.g. unload/reload `snd-hda-intel` and related modules, or reboot). + +## Kernel compatibility + +Recent work in this repository targets **modern kernels (6.17+)** where the ALSA HDA stack uses: + +- `struct hda_codec_driver` with `probe` / `remove` instead of the old `codec->patch_ops` / `.free` callbacks +- `snd_hda_gen_remove()` instead of the former `snd_hda_gen_free()` export (a compatibility wrapper in `hda_generic.h` maps the old name for this module) + +The module has been verified to build and run on **6.18.x** (e.g. Pop!_OS). Older kernels may still work if the symbols and APIs match; if not, use a branch or commit intended for that era. + +## Tested on + +| Environment | Kernel | Notes | +|-------------|--------|--------| +| **Pop!_OS** | `6.18.7-76061807-generic` | `make` and `sudo make install` succeed; audio confirmed working after reload/reboot as needed on the test machine. | + +Always install **kernel headers** that match your running kernel (`linux-headers-$(uname -r)` on Debian-based systems). + +## Development + +The **entire set of changes** in this repository as published here—the **kernel 6.17+ HDA API port** (`hda_codec_driver`, variant ops, `snd_hda_gen_remove` compatibility), **Makefile** / **`.gitignore`** updates, **README**, and related integration—was **written and iterated using [Cursor](https://cursor.com)** (AI-assisted editor). The CS8409 codec logic and Apple extensions remain **derived from Cirrus Logic and the Linux ALSA HDA codebase**; original **SPDX** and **copyright** lines in the source files still apply. + +## Configuration flags (Makefile) + +`KBUILD_EXTRA_CFLAGS` enables, among other things: + +- `APPLE_CODECS` — Apple-specific paths in `patch_cirrus_apple.h` +- `APPLE_PINSENSE_FIXUP` +- `CONFIG_SND_HDA_RECONFIG` + +A debug-oriented line is commented out in the `Makefile` (extra logging defines). + +## License + +The sources are under **GPL-2.0-or-later** (see SPDX headers in the individual files). This tree includes code attributed to Cirrus Logic and Takashi Iwai / the ALSA project, consistent with the upstream HDA codec drivers. + +## Troubleshooting + +| Issue | What to check | +|--------|----------------| +| `No rule to make target .../build` | Install kernel headers for `uname -r`. | +| Many errors about missing `cs8409_spec` fields | Build with this repo’s `make` (see above), not bare kbuild without `CFLAGS_MODULE`. | +| `modpost: "snd_hda_gen_free" undefined` | Use a tree that includes the `hda_generic.h` wrapper for `snd_hda_gen_remove` (kernel **≥ 6.17**). | +| Compiler differs from kernel | Usually a warning only; same major `gcc` as the distro kernel is ideal. | + +## Contributing + +Pull requests and reports against specific **kernel version** and **hardware** (PCI subsystem ID, machine model) help keep this module useful for others. diff --git a/patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h b/patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h new file mode 100644 index 0000000..87af3d8 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai + */ + +#ifndef __SOUND_HDA_AUTO_PARSER_H +#define __SOUND_HDA_AUTO_PARSER_H + +#include "hda_local.h" + +/* + * Helper for automatic pin configuration + */ + +enum { + AUTO_PIN_MIC, + AUTO_PIN_LINE_IN, + AUTO_PIN_CD, + AUTO_PIN_AUX, + AUTO_PIN_LAST +}; + +enum { + AUTO_PIN_LINE_OUT, + AUTO_PIN_SPEAKER_OUT, + AUTO_PIN_HP_OUT +}; + +#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS +#define AUTO_CFG_MAX_INS 18 + +struct auto_pin_cfg_item { + hda_nid_t pin; + int type; + unsigned int is_headset_mic:1; + unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ + unsigned int has_boost_on_pin:1; + int order; +}; + +struct auto_pin_cfg; +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input); +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp); + +enum { + INPUT_PIN_ATTR_UNUSED, /* pin not connected */ + INPUT_PIN_ATTR_INT, /* internal mic/line-in */ + INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ + INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ + INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, +}; + +int snd_hda_get_input_pin_attr(unsigned int def_conf); + +struct auto_pin_cfg { + int line_outs; + /* sorted in the order of Front/Surr/CLFE/Side */ + hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; + int speaker_outs; + hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; + int hp_outs; + int line_out_type; /* AUTO_PIN_XXX_OUT */ + hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; + int num_inputs; + struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; + int dig_outs; + hda_nid_t dig_out_pins[2]; + hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; + int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ + int dig_in_type; /* HDA_PCM_TYPE_XXX */ +}; + +/* bit-flags for snd_hda_parse_pin_def_config() behavior */ +#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ +#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ +#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */ +#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */ + +int snd_hda_parse_pin_defcfg(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + const hda_nid_t *ignore_nids, + unsigned int cond_flags); + +/* older function */ +#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ + snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) + +static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? + cfg->line_outs : cfg->hp_outs; +} +static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? + cfg->line_out_pins : cfg->hp_pins; +} +static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? + cfg->line_outs : cfg->speaker_outs; +} +static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? + cfg->line_out_pins : cfg->speaker_pins; +} + +#endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/patches/snd-hda-codec-cs8409/vendor/hda_generic.h b/patches/snd-hda-codec-cs8409/vendor/hda_generic.h new file mode 100644 index 0000000..cc8bc2d --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/hda_generic.h @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Generic BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai + */ + +#ifndef __SOUND_HDA_GENERIC_H +#define __SOUND_HDA_GENERIC_H + +#include +#include +#include "hda_auto_parser.h" + +struct hda_jack_callback; + +/* table entry for multi-io paths */ +struct hda_multi_io { + hda_nid_t pin; /* multi-io widget pin NID */ + hda_nid_t dac; /* DAC to be connected */ + unsigned int ctl_in; /* cached input-pin control value */ +}; + +/* Widget connection path + * + * For output, stored in the order of DAC -> ... -> pin, + * for input, pin -> ... -> ADC. + * + * idx[i] contains the source index number to select on of the widget path[i]; + * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ + +#define MAX_NID_PATH_DEPTH 10 + +enum { + NID_PATH_VOL_CTL, + NID_PATH_MUTE_CTL, + NID_PATH_BOOST_CTL, + NID_PATH_NUM_CTLS +}; + +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ + bool active:1; /* activated by driver */ + bool pin_enabled:1; /* pins are enabled */ + bool pin_fixed:1; /* path with fixed pin */ + bool stream_enabled:1; /* stream is active */ +}; + +/* mic/line-in auto switching entry */ + +#define MAX_AUTO_MIC_PINS 3 + +struct automic_entry { + hda_nid_t pin; /* pin */ + int idx; /* imux index, -1 = invalid */ + unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ +}; + +/* active stream id */ +enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; + +/* PCM hook action */ +enum { + HDA_GEN_PCM_ACT_OPEN, + HDA_GEN_PCM_ACT_PREPARE, + HDA_GEN_PCM_ACT_CLEANUP, + HDA_GEN_PCM_ACT_CLOSE, +}; + +/* DAC assignment badness table */ +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +}; + +extern const struct badness_table hda_main_out_badness; +extern const struct badness_table hda_extra_out_badness; + +struct hda_gen_spec { + char stream_name_analog[32]; /* analog PCM stream */ + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + + char stream_name_alt_analog[32]; /* alternative analog PCM stream */ + const struct hda_pcm_stream *stream_analog_alt_playback; + const struct hda_pcm_stream *stream_analog_alt_capture; + + char stream_name_digital[32]; /* digital PCM stream */ + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* PCM */ + unsigned int active_streams; + struct mutex pcm_mutex; + + /* playback */ + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ + hda_nid_t alt_dac_nid; + hda_nid_t follower_dig_outs[3]; /* optional - for auto-parsing */ + int dig_out_type; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t adc_nids[AUTO_CFG_MAX_INS]; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ + hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */ + const char *input_labels[HDA_MAX_NUM_INPUTS]; + int input_label_idxs[HDA_MAX_NUM_INPUTS]; + + /* capture setup for dynamic dual-adc switch */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* capture source */ + struct hda_input_mux input_mux; + unsigned int cur_mux[3]; + + /* channel model */ + /* min_channel_count contains the minimum channel count for primary + * outputs. When multi_ios is set, the channels can be configured + * between min_channel_count and (min_channel_count + multi_ios * 2). + * + * ext_channel_count contains the current channel count of the primary + * out. This varies in the range above. + * + * Meanwhile, const_channel_count is the channel count for all outputs + * including headphone and speakers. It's a constant value, and the + * PCM is set up as max(ext_channel_count, const_channel_count). + */ + int min_channel_count; /* min. channel count for primary out */ + int ext_channel_count; /* current channel count for primary */ + int const_channel_count; /* channel count for all */ + + /* PCM information */ + struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */ + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + /* shared hp/mic */ + hda_nid_t shared_mic_vref_pin; + hda_nid_t hp_mic_pin; + int hp_mic_mux_idx; + + /* DAC/ADC lists */ + int num_all_dacs; + hda_nid_t all_dacs[16]; + int num_all_adcs; + hda_nid_t all_adcs[AUTO_CFG_MAX_INS]; + + /* path list */ + struct snd_array paths; + + /* path indices */ + int out_paths[AUTO_CFG_MAX_OUTS]; + int hp_paths[AUTO_CFG_MAX_OUTS]; + int speaker_paths[AUTO_CFG_MAX_OUTS]; + int aamix_out_paths[3]; + int digout_paths[AUTO_CFG_MAX_OUTS]; + int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS]; + int loopback_paths[HDA_MAX_NUM_INPUTS]; + int loopback_merge_path; + int digin_path; + + /* auto-mic stuff */ + int am_num_entries; + struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; + + /* for pin sensing */ + /* current status; set in hda_generic.c */ + unsigned int hp_jack_present:1; + unsigned int line_jack_present:1; + unsigned int speaker_muted:1; /* current status of speaker mute */ + unsigned int line_out_muted:1; /* current status of LO mute */ + + /* internal states of automute / autoswitch behavior */ + unsigned int auto_mic:1; + unsigned int automute_speaker:1; /* automute speaker outputs */ + unsigned int automute_lo:1; /* automute LO outputs */ + + /* capabilities detected by parser */ + unsigned int detect_hp:1; /* Headphone detection enabled */ + unsigned int detect_lo:1; /* Line-out detection enabled */ + unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ + unsigned int automute_lo_possible:1; /* there are line outs and HP */ + + /* additional parameters set by codec drivers */ + unsigned int master_mute:1; /* master mute over all */ + unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */ + + /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ + unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ + unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ + + /* other parse behavior flags */ + unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ + unsigned int hp_mic:1; /* Allow HP as a mic-in */ + unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */ + unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int no_multi_io:1; /* Don't try multi I/O config */ + unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ + unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ + unsigned int own_eapd_ctl:1; /* set EAPD by own function */ + unsigned int keep_eapd_on:1; /* don't turn off EAPD automatically */ + unsigned int vmaster_mute_led:1; /* add SPK-LED flag to vmaster mute switch */ + unsigned int mic_mute_led:1; /* add MIC-LED flag to capture mute switch */ + unsigned int indep_hp:1; /* independent HP supported */ + unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ + unsigned int add_stereo_mix_input:2; /* add aamix as a capture src */ + unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */ + unsigned int power_down_unused:1; /* power down unused widgets */ + unsigned int dac_min_mute:1; /* minimal = mute for DACs */ + unsigned int suppress_vmaster:1; /* don't create vmaster kctls */ + + /* other internal flags */ + unsigned int no_analog:1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int indep_hp_enabled:1; /* independent HP enabled */ + unsigned int have_aamix_ctl:1; + unsigned int hp_mic_jack_modes:1; + unsigned int skip_verbs:1; /* don't apply verbs at snd_hda_gen_init() */ + + /* additional mute flags (only effective with auto_mute_via_amp=1) */ + u64 mute_bits; + + /* bitmask for skipping volume controls */ + u64 out_vol_mask; + + /* badness tables for output path evaluations */ + const struct badness_table *main_out_badness; + const struct badness_table *extra_out_badness; + + /* preferred pin/DAC pairs; an array of paired NIDs */ + const hda_nid_t *preferred_dacs; + + /* loopback mixing mode */ + bool aamix_mode; + + /* digital beep */ + hda_nid_t beep_nid; + + /* for virtual master */ + hda_nid_t vmaster_nid; + unsigned int vmaster_tlv[4]; + struct hda_vmaster_mute_hook vmaster_mute; + + struct hda_loopback_check loopback; + struct snd_array loopback_list; + + /* multi-io */ + int multi_ios; + struct hda_multi_io multi_io[4]; + + /* hooks */ + void (*init_hook)(struct hda_codec *codec); + void (*automute_hook)(struct hda_codec *codec); + void (*cap_sync_hook)(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + + /* PCM hooks */ + void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + + /* automute / autoswitch hooks */ + void (*hp_automute_hook)(struct hda_codec *codec, + struct hda_jack_callback *cb); + void (*line_automute_hook)(struct hda_codec *codec, + struct hda_jack_callback *cb); + void (*mic_autoswitch_hook)(struct hda_codec *codec, + struct hda_jack_callback *cb); + + /* leds */ + struct led_classdev *led_cdevs[NUM_AUDIO_LEDS]; +}; + +/* values for add_stereo_mix_input flag */ +enum { + HDA_HINT_STEREO_MIX_DISABLE, /* No stereo mix input */ + HDA_HINT_STEREO_MIX_ENABLE, /* Add stereo mix input */ + HDA_HINT_STEREO_MIX_AUTO, /* Add only if auto-mic is disabled */ +}; + +int snd_hda_gen_spec_init(struct hda_gen_spec *spec); + +int snd_hda_gen_init(struct hda_codec *codec); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0) +void snd_hda_gen_remove(struct hda_codec *codec); +static inline void snd_hda_gen_free(struct hda_codec *codec) +{ + snd_hda_gen_remove(codec); +} +#else +void snd_hda_gen_free(struct hda_codec *codec); +#endif + +int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); +struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); +struct nid_path * +snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int anchor_nid); +void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix); + +struct snd_kcontrol_new * +snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp); + +int snd_hda_gen_parse_auto_config(struct hda_codec *codec, + struct auto_pin_cfg *cfg); +int snd_hda_gen_build_controls(struct hda_codec *codec); +int snd_hda_gen_build_pcms(struct hda_codec *codec); + +/* standard jack event callbacks */ +void snd_hda_gen_hp_automute(struct hda_codec *codec, + struct hda_jack_callback *jack); +void snd_hda_gen_line_automute(struct hda_codec *codec, + struct hda_jack_callback *jack); +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, + struct hda_jack_callback *jack); +void snd_hda_gen_update_outputs(struct hda_codec *codec); + +int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); +unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state); +void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); +int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); + +int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)); +int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)); +bool snd_hda_gen_shutup_speakers(struct hda_codec *codec); + +#endif /* __SOUND_HDA_GENERIC_H */ diff --git a/patches/snd-hda-codec-cs8409/vendor/hda_jack.h b/patches/snd-hda-codec-cs8409/vendor/hda_jack.h new file mode 100644 index 0000000..ff7d289 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/hda_jack.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Jack-detection handling for HD-audio + * + * Copyright (c) 2011 Takashi Iwai + */ + +#ifndef __SOUND_HDA_JACK_H +#define __SOUND_HDA_JACK_H + +#include +#include + +struct auto_pin_cfg; +struct hda_jack_tbl; +struct hda_jack_callback; + +typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *); + +struct hda_jack_callback { + hda_nid_t nid; + int dev_id; + hda_jack_callback_fn func; + unsigned int private_data; /* arbitrary data */ + unsigned int unsol_res; /* unsolicited event bits */ + struct hda_jack_tbl *jack; /* associated jack entry */ + struct hda_jack_callback *next; +}; + +struct hda_jack_tbl { + hda_nid_t nid; + int dev_id; + unsigned char tag; /* unsol event tag */ + struct hda_jack_callback *callback; + /* jack-detection stuff */ + unsigned int pin_sense; /* cached pin-sense value */ + unsigned int jack_detect:1; /* capable of jack-detection? */ + unsigned int jack_dirty:1; /* needs to update? */ + unsigned int phantom_jack:1; /* a fixed, always present port? */ + unsigned int block_report:1; /* in a transitional state - do not report to userspace */ + hda_nid_t gating_jack; /* valid when gating jack plugged */ + hda_nid_t gated_jack; /* gated is dependent on this jack */ + hda_nid_t key_report_jack; /* key reports to this jack */ + int type; + int button_state; + struct snd_jack *jack; +}; + +struct hda_jack_keymap { + enum snd_jack_types type; + int key; +}; + +struct hda_jack_tbl * +snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id); + +/** + * snd_hda_jack_tbl_get - query the jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to refer to + */ +static inline struct hda_jack_tbl * +snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_tbl_get_mst(codec, nid, 0); +} + +struct hda_jack_tbl * +snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, + unsigned char tag, int dev_id); + +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec); +void snd_hda_jack_tbl_clear(struct hda_codec *codec); + +void snd_hda_jack_set_dirty_all(struct hda_codec *codec); + +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, + int dev_id); + +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); + +/** + * snd_hda_jack_detect_enable - enable the jack-detection + * @codec: the HDA codec + * @nid: pin NID to enable + * @func: callback function to register + * + * 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(). + */ +static inline struct hda_jack_callback * +snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, + hda_jack_callback_fn cb) +{ + return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb); +} + +int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_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); + +void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid, + int button_state); + +u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id); + +/* the jack state returned from snd_hda_jack_detect_state() */ +enum { + HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM, +}; + +int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid, + int dev_id); + +/** + * snd_hda_jack_detect_state - query pin Presence Detect status + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Query and return the pin's Presence Detect status, as either + * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. + */ +static inline int +snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_detect_state_mst(codec, nid, 0); +} + +/** + * snd_hda_jack_detect_mst - Detect the jack + * @codec: the HDA codec + * @nid: pin NID to check jack detection + * @dev_id: pin device entry id + */ +static inline bool +snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ + return snd_hda_jack_detect_state_mst(codec, nid, dev_id) != + HDA_JACK_NOT_PRESENT; +} + +/** + * snd_hda_jack_detect - Detect the jack + * @codec: the HDA codec + * @nid: pin NID to check jack detection + */ +static inline bool +snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_detect_mst(codec, nid, 0); +} + +bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); + +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); + +/** + * snd_hda_jack_add_kctl - Add a kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID to assign + * @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. + */ +static inline int +snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, + const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap) +{ + return snd_hda_jack_add_kctl_mst(codec, nid, 0, + name, phantom_jack, type, keymap); +} + +int snd_hda_jack_add_kctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg); + +void snd_hda_jack_report_sync(struct hda_codec *codec); + +void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res); + +void snd_hda_jack_poll_all(struct hda_codec *codec); + +#endif /* __SOUND_HDA_JACK_H */ diff --git a/patches/snd-hda-codec-cs8409/vendor/hda_local.h b/patches/snd-hda-codec-cs8409/vendor/hda_local.h new file mode 100644 index 0000000..763f79f --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/hda_local.h @@ -0,0 +1,763 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Local helper functions + * + * Copyright (c) 2004 Takashi Iwai + */ + +#ifndef __SOUND_HDA_LOCAL_H +#define __SOUND_HDA_LOCAL_H + +/* We abuse kcontrol_new.subdev field to pass the NID corresponding to + * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, + * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. + * + * Note that the subdevice field is cleared again before the real registration + * in snd_hda_ctl_add(), so that this value won't appear in the outside. + */ +#define HDA_SUBDEV_NID_FLAG (1U << 31) +#define HDA_SUBDEV_AMP_FLAG (1U << 30) + +/* + * for mixer controls + */ +#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ + ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) +#define HDA_AMP_VAL_MIN_MUTE (1<<29) +#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ + HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) +/* mono volume with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .info = snd_hda_mixer_amp_volume_info, \ + .get = snd_hda_mixer_amp_volume_get, \ + .put = snd_hda_mixer_amp_volume_put, \ + .tlv = { .c = snd_hda_mixer_amp_tlv }, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags } +/* stereo volume with index */ +#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0) +/* mono volume */ +#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0) +/* stereo volume */ +#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) +/* stereo volume with min=mute */ +#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \ + HDA_AMP_VAL_MIN_MUTE) +/* mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +/* stereo mute switch with index */ +#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) +/* mono mute switch */ +#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* stereo mute switch */ +#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get_beep, \ + .put = snd_hda_mixer_amp_switch_put_beep, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#else +/* no digital beep - just the standard one */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ +/* special beep mono mute switch */ +#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* special beep stereo mute switch */ +#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) + +extern const char *snd_hda_pcm_type_name[]; + +int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv); +int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +#ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +#endif +/* lowlevel accessor with caching; use carefully */ +#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \ + snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx) +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val); +int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, + int direction, int idx, int mask, int val); +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val); +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val); +void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int *tlv); +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name); +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char * const *followers, + const char *suffix, bool init_follower_vol, + unsigned int access, struct snd_kcontrol **ctl_ret); +#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ + __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) +int snd_hda_codec_reset(struct hda_codec *codec); +void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); + +#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) + +struct hda_vmaster_mute_hook { + /* below two fields must be filled by the caller of + * snd_hda_add_vmaster_hook() beforehand + */ + struct snd_kcontrol *sw_kctl; + void (*hook)(void *, int); + /* below are initialized automatically */ + struct hda_codec *codec; +}; + +int snd_hda_add_vmaster_hook(struct hda_codec *codec, + struct hda_vmaster_mute_hook *hook); +void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook); + +/* amp value bits */ +#define HDA_AMP_MUTE 0x80 +#define HDA_AMP_UNMUTE 0x00 +#define HDA_AMP_VOLMASK 0x7f + +/* + * SPDIF I/O + */ +int snd_hda_create_dig_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid, int type); +#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \ + snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF) +int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); + +/* + * input MUX helper + */ +#define HDA_MAX_NUM_INPUTS 36 +struct hda_input_mux_item { + char label[32]; + unsigned int index; +}; +struct hda_input_mux { + unsigned int num_items; + struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS]; +}; + +int snd_hda_input_mux_info(const struct hda_input_mux *imux, + struct snd_ctl_elem_info *uinfo); +int snd_hda_input_mux_put(struct hda_codec *codec, + const struct hda_input_mux *imux, + struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, + unsigned int *cur_val); +int snd_hda_add_imux_item(struct hda_codec *codec, + struct hda_input_mux *imux, const char *label, + int index, int *type_idx); + +/* + * Multi-channel / digital-out PCM helper + */ + +enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */ +enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */ + +#define HDA_MAX_OUTS 5 + +struct hda_multi_out { + int num_dacs; /* # of DACs, must be more than 1 */ + const hda_nid_t *dac_nids; /* DAC list */ + hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ + hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */ + hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */ + hda_nid_t dig_out_nid; /* digital out audio widget */ + const hda_nid_t *follower_dig_outs; + int max_channels; /* currently supported analog channels */ + int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ + int no_share_stream; /* don't share a stream with multiple pins */ + int share_spdif; /* share SPDIF pin */ + /* PCM information for both analog and SPDIF DACs */ + unsigned int analog_rates; + unsigned int analog_maxbps; + u64 analog_formats; + unsigned int spdif_rates; + unsigned int spdif_maxbps; + u64 spdif_formats; +}; + +int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout); +int snd_hda_multi_out_dig_open(struct hda_codec *codec, + struct hda_multi_out *mout); +int snd_hda_multi_out_dig_close(struct hda_codec *codec, + struct hda_multi_out *mout); +int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, + struct hda_multi_out *mout, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream); +int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, + struct hda_multi_out *mout); +int snd_hda_multi_out_analog_open(struct hda_codec *codec, + struct hda_multi_out *mout, + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo); +int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, + struct hda_multi_out *mout, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream); +int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, + struct hda_multi_out *mout); + +/* + * generic proc interface + */ +#ifdef CONFIG_SND_PROC_FS +int snd_hda_codec_proc_new(struct hda_codec *codec); +#else +static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } +#endif + +#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 +void snd_print_pcm_bits(int pcm, char *buf, int buflen); + +/* + * Misc + */ +int snd_hda_add_new_ctls(struct hda_codec *codec, + const struct snd_kcontrol_new *knew); + +/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained:1; /* call the chained fixup(s) after this */ + bool chained_before:1; /* call the chained fixup(s) before this */ + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* + * extended form of snd_pci_quirk: + * for PCI SSID matching, use SND_PCI_QUIRK() like before; + * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead + */ +struct hda_quirk { + unsigned short subvendor; /* PCI subvendor ID */ + unsigned short subdevice; /* PCI subdevice ID */ + unsigned short subdevice_mask; /* bitmask to match */ + bool match_codec_ssid; /* match only with codec SSID */ + int value; /* value */ +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *name; /* name of the device (optional) */ +#endif +}; + +#ifdef CONFIG_SND_DEBUG_VERBOSE +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\ + .match_codec_ssid = true } +#else +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \ + .match_codec_ssid = true } +#endif + +struct snd_hda_pin_quirk { + unsigned int codec; /* Codec vendor/device ID */ + unsigned short subvendor; /* PCI subvendor ID */ + const struct hda_pintbl *pins; /* list of matching pins */ +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *name; +#endif + int value; /* quirk value */ +}; + +#ifdef CONFIG_SND_DEBUG_VERBOSE + +#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \ + { .codec = _codec,\ + .subvendor = _subvendor,\ + .name = _name,\ + .value = _value,\ + .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \ + } +#else + +#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \ + { .codec = _codec,\ + .subvendor = _subvendor,\ + .value = _value,\ + .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \ + } + +#endif + +#define HDA_FIXUP_ID_NOT_SET -1 +#define HDA_FIXUP_ID_NO_FIXUP -2 + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, + HDA_FIXUP_PINCTLS, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, + HDA_FIXUP_ACT_FREE, +}; + +int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); +void snd_hda_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct hda_quirk *quirk, + const struct hda_fixup *fixlist); +void snd_hda_pick_pin_fixup(struct hda_codec *codec, + const struct snd_hda_pin_quirk *pin_quirk, + const struct hda_fixup *fixlist, + bool match_all_pins); + +/* helper macros to retrieve pin default-config values */ +#define get_defcfg_connect(cfg) \ + ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) +#define get_defcfg_association(cfg) \ + ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) +#define get_defcfg_location(cfg) \ + ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +#define get_defcfg_sequence(cfg) \ + (cfg & AC_DEFCFG_SEQUENCE) +#define get_defcfg_device(cfg) \ + ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define get_defcfg_misc(cfg) \ + ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) + +/* amp values */ +#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) +#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define AMP_OUT_ZERO 0xb000 +/* pinctl values */ +#define PIN_IN (AC_PINCTL_IN_EN) +#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) +#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50) +#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) +#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80) +#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) +#define PIN_OUT (AC_PINCTL_OUT_EN) +#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) +#define PIN_HP_AMP (AC_PINCTL_HP_EN) + +unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); +unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, + hda_nid_t pin, unsigned int val); +int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool cached); + +/** + * _snd_hda_set_pin_ctl - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * This function sets the pin-control value to the given pin, but + * filters out the invalid pin-control bits when the pin has no such + * capabilities. For example, when PIN_HP is passed but the pin has no + * HP-drive capability, the HP bit is omitted. + * + * The function doesn't check the input VREF capability bits, though. + * Use snd_hda_get_default_vref() to guess the right value. + * Also, this function is only for analog pins, not for HDMI pins. + */ +static inline int +snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, false); +} + +/** + * snd_hda_set_pin_ctl_cache - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * Just like snd_hda_set_pin_ctl() but write to cache as well. + */ +static inline int +snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, + unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, true); +} + +int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, + unsigned int val); + +#define for_each_hda_codec_node(nid, codec) \ + for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++) + +/* Set the codec power_state flag to indicate to allow unsol event handling; + * see hda_codec_unsol_event() in hda_bind.c. Calling this might confuse the + * state tracking, so use with care. + */ +static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec) +{ + codec->core.dev.power.power_state = PMSG_ON; +} + +/* + * get widget capabilities + */ +static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) +{ + if (nid < codec->core.start_nid || + nid >= codec->core.start_nid + codec->core.num_nodes) + return 0; + return codec->wcaps[nid - codec->core.start_nid]; +} + +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +static inline unsigned int get_wcaps_channels(u32 wcaps) +{ + unsigned int chans; + + chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; + chans = ((chans << 1) | 1) + 1; + + return chans; +} + +static inline void snd_hda_override_wcaps(struct hda_codec *codec, + hda_nid_t nid, u32 val) +{ + if (nid >= codec->core.start_nid && + nid < codec->core.start_nid + codec->core.num_nodes) + codec->wcaps[nid - codec->core.start_nid] = val; +} + +u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); +int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int caps); +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ +static inline u32 +snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + +} + +/** + * snd_hda_override_pin_caps - Override the pin capabilities + * @codec: the CODEC + * @nid: the NID to override + * @caps: the capability bits to set + * + * Override the cached PIN capabilitiy bits value by the given one. + * + * Returns zero if successful or a negative error code. + */ +static inline int +snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, + unsigned int caps) +{ + return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps); +} + +bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits); + +#define nid_has_mute(codec, nid, dir) \ + snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) +#define nid_has_volume(codec, nid, dir) \ + snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + + +/* flags for hda_nid_item */ +#define HDA_NID_ITEM_AMP (1<<0) + +struct hda_nid_item { + struct snd_kcontrol *kctl; + unsigned int index; + hda_nid_t nid; + unsigned short flags; +}; + +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl); +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid); +void snd_hda_ctls_clear(struct hda_codec *codec); + +/* + * hwdep interface + */ +#ifdef CONFIG_SND_HDA_HWDEP +int snd_hda_create_hwdep(struct hda_codec *codec); +#else +static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } +#endif + +void snd_hda_sysfs_init(struct hda_codec *codec); +void snd_hda_sysfs_clear(struct hda_codec *codec); + +extern const struct attribute_group *snd_hda_dev_attr_groups[]; + +#ifdef CONFIG_SND_HDA_RECONFIG +const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); +int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); +int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp); +#else +static inline +const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) +{ + return NULL; +} + +static inline +int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) +{ + return -ENOENT; +} + +static inline +int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) +{ + return -ENOENT; +} +#endif + +/* + * power-management + */ + +void snd_hda_schedule_power_save(struct hda_codec *codec); + +struct hda_amp_list { + hda_nid_t nid; + unsigned char dir; + unsigned char idx; +}; + +struct hda_loopback_check { + const struct hda_amp_list *amplist; + int power_on; +}; + +int snd_hda_check_amp_list_power(struct hda_codec *codec, + struct hda_loopback_check *check, + hda_nid_t nid); + +/* check whether the actual power state matches with the target state */ +static inline bool +snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int target_state) +{ + return snd_hdac_check_power_state(&codec->core, nid, target_state); +} + +static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec, + hda_nid_t nid, + unsigned int target_state) +{ + return snd_hdac_sync_power_state(&codec->core, nid, target_state); +} +unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state); + +void snd_hda_codec_shutdown(struct hda_codec *codec); + +/* + * AMP control callbacks + */ +/* retrieve parameters from private_value */ +#define get_amp_nid_(pv) ((pv) & 0xffff) +#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) +#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) +#define get_amp_direction_(pv) (((pv) >> 18) & 0x1) +#define get_amp_direction(kc) get_amp_direction_((kc)->private_value) +#define get_amp_index_(pv) (((pv) >> 19) & 0xf) +#define get_amp_index(kc) get_amp_index_((kc)->private_value) +#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) +#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) + +/* + * enum control helper + */ +int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo, + int num_items, const char * const *texts); +#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \ + snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL) + +/* + * CEA Short Audio Descriptor data + */ +struct cea_sad { + int channels; + int format; /* (format == 0) indicates invalid SAD */ + int rates; + int sample_bits; /* for LPCM */ + int max_bitrate; /* for AC3...ATRAC */ + int profile; /* for WMAPRO */ +}; + +#define ELD_FIXED_BYTES 20 +#define ELD_MAX_SIZE 256 +#define ELD_MAX_MNL 16 +#define ELD_MAX_SAD 16 + +/* + * ELD: EDID Like Data + */ +struct parsed_hdmi_eld { + /* + * all fields will be cleared before updating ELD + */ + int baseline_len; + int eld_ver; + int cea_edid_ver; + char monitor_name[ELD_MAX_MNL + 1]; + int manufacture_id; + int product_id; + u64 port_id; + int support_hdcp; + int support_ai; + int conn_type; + int aud_synch_delay; + int spk_alloc; + int sad_count; + struct cea_sad sad[ELD_MAX_SAD]; +}; + +struct hdmi_eld { + bool monitor_present; + bool eld_valid; + int eld_size; + char eld_buffer[ELD_MAX_SIZE]; + struct parsed_hdmi_eld info; +}; + +int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); +int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size); +int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e, + const unsigned char *buf, int size); +void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e); +void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, + struct hda_pcm_stream *hinfo); + +int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, + bool rev3_or_later); + +#ifdef CONFIG_SND_PROC_FS +void snd_hdmi_print_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer, + hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid); +void snd_hdmi_write_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer); +#endif + +#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); + +void snd_hda_codec_display_power(struct hda_codec *codec, bool enable); + +/* + */ +#define codec_err(codec, fmt, args...) \ + dev_err(hda_codec_dev(codec), fmt, ##args) +#define codec_warn(codec, fmt, args...) \ + dev_warn(hda_codec_dev(codec), fmt, ##args) +#define codec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define codec_dbg(codec, fmt, args...) \ + dev_dbg(hda_codec_dev(codec), fmt, ##args) + +#endif /* __SOUND_HDA_LOCAL_H */ diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h new file mode 100644 index 0000000..1aae4fd --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h @@ -0,0 +1,3085 @@ +//#include +#include +#include + +#include + +//#include "hda_generic.h" + +//#include "patch_cs8409.h" + + + +// define some explicit debugging print functions +// under flag control so can be easily turned off + + +#ifdef MYSOUNDDEBUGFULL +#define mycodec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mycodec_i2c_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mydev_info(codecdev, fmt, args...) \ + dev_info(codecdev, fmt, ##args) +#define mycodec_dbg(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define myprintk_dbg(fmt, args...) \ + printk(fmt, ##args) +#define myprintk(fmt, args...) \ + printk(fmt, ##args) +#else +#define mycodec_dbg(...) +#define myprintk_dbg(...) +#ifdef MYSOUNDDEBUG +#define mycodec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mycodec_i2c_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mydev_info(codecdev, fmt, args...) \ + dev_info(codecdev, fmt, ##args) +#define myprintk(fmt, args...) \ + printk(fmt, ##args) +#else +#define mycodec_info(...) +#define mycodec_i2c_info(...) +#define mydev_info(...) +#define myprintk(...) +#endif +#endif + + +/* + + info for enum for checking initial assignment + +enum { + AC_JACK_LINE_OUT, 0x0 + AC_JACK_SPEAKER, 0x1 + AC_JACK_HP_OUT, 0x2 + AC_JACK_CD, 0x3 + AC_JACK_SPDIF_OUT, 0x4 + AC_JACK_DIG_OTHER_OUT, 0x5 + AC_JACK_MODEM_LINE_SIDE, 0x6 + AC_JACK_MODEM_HAND_SIDE, 0x7 + AC_JACK_LINE_IN, 0x8 + AC_JACK_AUX, 0x9 + AC_JACK_MIC_IN, 0xa + AC_JACK_TELEPHONY, 0xb + AC_JACK_SPDIF_IN, 0xc + AC_JACK_DIG_OTHER_IN, 0xd + AC_JACK_OTHER = 0xf, 0xf +}; + */ + + +/* + + it appears from the cs42l42 definitions in patch_cs8409.h that + each nid is associated with a specific Audio Serial Port + + nids as used in Apple + output + speaker 0x002 -> 0x024 CS8409_PIN_ASP1_OUT_A -> CS8409_PIN_ASP1_TRANSMITTER_A + speaker 0x003 -> 0x025 CS8409_PIN_ASP1_OUT_B -> CS8409_PIN_ASP1_TRANSMITTER_B + headphones 0x00a -> 0x02c CS8409_PIN_ASP2_OUT_A -> CS8409_PIN_ASP2_TRANSMITTER_A + input + headset mike 0x03c -> 0x01a CS8409_PIN_ASP2_RECEIVER_A -> CS8409_PIN_ASP2_IN_A + macbook pros + internal mike 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1 + linein 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2 + imacs + linein 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1 + internal mike 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2 + + */ + + +/* CS8409 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +// for the moment leave my old definition names but map to the new module enum values +#define CS8409_IDX_DEV_CFG CS8409_PIN_AFG +#define CS8409_VENDOR_NID CS8409_PIN_VENDOR_WIDGET +#define CS8409_BEEP_NID CS8409_PIN_BEEP_GEN +#else +#define CS8409_IDX_DEV_CFG 0x01 +#define CS8409_VENDOR_NID 0x47 +#define CS8409_BEEP_NID 0x46 +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +// nid devs based on Dell fixups +#define CS8409_CS42L83_HP_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A +#define CS8409_CS42L83_HP_MIC_PIN_NID CS8409_PIN_ASP2_RECEIVER_A +#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID CS8409_PIN_DMIC1_IN +#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID CS8409_PIN_DMIC2_IN +#define CS8409_CS42L83_IMAC_MIC_PIN_NID CS8409_PIN_DMIC2_IN +#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID CS8409_PIN_DMIC1_IN +#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC2 +#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC1 + +// add in coding based on Dell fixups + +#define CS42L83_I2C_ADDR 0x90 // for some reason given as (0x48 << 1) +#define CS8409_CS42L83_RESET 0x02 // gpio interrupt mask - see cs42l83_external_control_GPIO +#define CS8409_CS42L83_INT 0x01 // gpio interrupt mask + +#else + +#define CS8409_CS42L83_HP_PIN_NID 0x2c +#define CS8409_CS42L83_HP_MIC_PIN_NID 0x3c +#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID 0x44 +#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID 0x45 +#define CS8409_CS42L83_IMAC_MIC_PIN_NID 0x45 +#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID 0x44 +#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID 0x23 +#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID 0x22 + +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +// this is a list of i2c commands for init +static const struct cs8409_i2c_param cs42l83_init_reg_seq[] = { + //{ 0x0000, 0x00 }, +}; + +struct sub_codec cs8409_cs42l83_codec = { + .addr = CS42L83_I2C_ADDR, + .reset_gpio = CS8409_CS42L83_RESET, + .irq_mask = CS8409_CS42L83_INT, + .init_seq = cs42l83_init_reg_seq, + .init_seq_num = ARRAY_SIZE(cs42l83_init_reg_seq), + .hp_jack_in = 0, + .mic_jack_in = 0, + .linein_jack_in = 0, + .paged = 1, + .suspended = 1, + .no_type_dect = 0, +}; + + +// define the fixed stream format + +const struct hda_pcm_stream cs42l83_apple_pcm_analog_playback = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ +}; + +const struct hda_pcm_stream cs42l83_apple_pcm_analog_capture = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ +}; + + +#endif + +/* + + register mask values collated from Microsoft cs4208_39.inf file plus the new Dell cs8409 module + + CS8409_DEV_CFG1, // reg 0x00 + 0xb008 +PLL1/2_EN, +I2C_EN - .inf file + 0xb000 +PLL1/2_EN - .inf file + 0x9008 +PLL1_EN, +I2C_EN - .inf file + 0x9000 +PLL1_EN - .inf file + CS8409_DEV_CFG2, // reg 0x01 + 0x0006 ASP1/2_EN = 0, ASP1/2_STP = 1 + 0x0066 ASP1/2_EN = 1, ASP1/2_STP = 1 + 0x0022 ASP1_EN = 1, ASP1_STP = 1 ? + 0x0044 ASP2_EN = 1, ASP2_STP = 1 ? + 0x0200 seen in Apple but unknown + CS8409_DEV_CFG3, // reg 0x02 + 0x0a80 ASP1/2_BUS_IDLE=10, +GPIO_I2C + 0x0280 seen in Apple ASP1_BUS_IDLE=10?, +GPIO_I2C + CS8409_ASP1_CLK_CTRL1, // reg 0x03 + 0x8000 ASP1: LCHI = 00h + CS8409_ASP1_CLK_CTRL2, // reg 0x04 + 0x28ff ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh + CS8409_ASP1_CLK_CTRL3, // reg 0x05 + 0x0062 ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 + 0b0110 0b0010 + so FSD = 0x0060 + 0x005a ASP1: MCEN = 0, FSD = 010, SCPOL_IN/OUT = 1, SCDIV = 1:4 + 0b0101 0b1010 + so FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV = 0x0002 + 0x0001 seen in Apple but what bit is this?? MCEN?? + CS8409_ASP2_CLK_CTRL1, // reg 0x06 + 0x8000 ASP1: LCHI = 00h + CS8409_ASP2_CLK_CTRL2, // reg 0x07 + 0x283f ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh + CS8409_ASP2_CLK_CTRL3, // reg 0x08 + 0x805c ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 + 0b0101 0b1100 + 5050 = 0x8000?, FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV=1:16 = 0x0004? + CS8409_DMIC_CFG, // reg 0x09 + 0x0023 DMIC1_MO=10b, DMIC1/2_SR=1 + 0x0033 seen in Apple + 0x00b3 seen in Apple + 0x01b3 seen in Apple + CS8409_BEEP_CFG, // reg 0x0a + + ASP2_Rx_NULL_INS_RMV, // reg 0x11 + 0x0001 seen in Apple + ASP2_Rx_RATE1, // reg 0x12 + 0xaccc seen in Apple + ASP2_Tx_NULL_INS_RMV, // reg 0x14 + 0x0100 seen in Apple + ASP2_Tx_RATE1, // reg 0x15 + 0xaaaa seen in Apple + ASP1_SYNC_CTRL, // reg 0x17 + 0x0000 seen in Apple sync converters + 0x0001 seen in Apple sync converters + 0x0002 seen in Apple sync converters + 0x0003 seen in Apple sync converters + ASP2_SYNC_CTRL, // reg 0x18 + 0x0000 seen in Apple sync converters + + the following registers are defined per output nid + from A to H for nid 0x02 to 0x09 ASP1 + from A to H for nid 0x0a to 0x11 ASP2 + the following registers are defined per input nid + from A to H for nid 0x12 to 0x19 ASP1 + from A to H for nid 0x1a to 0x21 ASP2 + + ASP1_A_TX_CTRL1 // reg 0x19 + nide 0x02 + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP1_A_TX_CTRL2 // reg 0x1a + nide 0x02 + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + ASP1_B_TX_CTRL1, // reg 0x1b + nide 0x03 + 0x0840 seen in Apple - on + 0x8840 seen in Apple - off + ASP1_B_TX_CTRL2, // reg 0x1c + nide 0x03 + 0x0860 seen in Apple - on + 0x8860 seen in Apple - off + + ASP2_A_TX_CTRL1, // reg 0x29 + nide 0x0a + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP2_A_TX_CTRL2, // reg 0x2a + nide 0x0a + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + + ASP2_A_RX_CTRL1, // reg 0x49 + nide 0x1a + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP2_A_RX_CTRL2, // reg 0x4a + nide 0x1a + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + + CS8409_ASP1_INTRN_STS, // reg 0x6b + 0x001f seen in Apple + CS8409_ASP2_INTRN_STS, // reg 0x6c + 0x001f seen in Apple + + CS8409_ASP_UNS_RESP_MASK, // reg 0x71 + 0x400f seen in Apple + 0x800f seen in Apple + 0xc00f seen in Apple + + CS8409_PAD_CFG_SLW_RATE_CTRL // reg 0x82 + it appears to contain 2 sort of separate items - the ASP1 and ASP2 enables and the DMIC1/DMIC2 SCL enables + 0xfc03 ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1/2_SCL_EN=1 (was DMIC1_SCL_EN in comments but thinks thats wrong given below) + 0xfc01 (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1) + 0xff03 (ASP1/2_xxx_EN = 1, DMIC1/2_SCL_EN = 1) + 0xfd02 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 1, ASP2_MCLK_EN = 0, DMIC2_SCL_EN = 1) + 0xfe03 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 0, ASP2_MCLK_EN = 1, DMIC1/2_SCL_EN = 1) + so (ASP1_MCLK_EN is 0x0100 and ASP2_MCLK_EN is 0x0200) + from the OSX codes we seem to have + 0xa800 ASP2_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + 0x5400 ASP1_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + + + */ + +/* + + Interrupt analysis (mainly from cs42l42 manual) + + actual button presses are 0x01, 0x02 and button release 0x10 + for 0x1b7c 0x02 is a short release for buttons, 0x08 is reserved + the mask bits for 0x1b7a seem to be 0xe7 for button detect defining 0x18 as the button detect interrupt(s) + and 0xdc for actual button interrupts + (0x1b79 is mask, 0x1b7b status; 0x1b7a is mask, 0x1b7c is presumed status, 0x131b is mask, 0x1308 status, + 0x1320 is mask, 0x130f status) + + 0x1b79 Detect Interrupt Mask 1 + 0x80 M_HSBIAS_SENSE + 0x40 M_TIP_SENSE_PLUG + 0x20 M_TIP_SENSE_UNPLUG + 0x1b7b Detect Interrupt Status 1 - assumed - not documented in cs42l42 but listed in fig 4-45 + 0x80 M_HSBIAS_SENSE - assumed + 0x40 M_TIP_SENSE_PLUG - assumed + 0x20 M_TIP_SENSE_UNPLUG - assumed + 0x1b7a Detect Interrupt Mask 2 (Buttons) + cs42l42 values documented: + 0x80 M_DETECT_TRUE_FALSE + 0x40 M_DETECT_FALSE_TRUE + 0x04 M_HSBIAS_HIZ + 0x02 M_SHORT_RELEASE + 0x01 M_SHORT_DETECTED + OSX button masks seen are 0xff, 0xe7 and 0xdc + so for 0xe7 mask status bits are 0x18 + so for 0xdc mask status bits are 0x23 + bits 0x38 are not documented for cs42l42 + 0xe7 is used for button detection + 0xdc is used for button responses + 0x1b7c Detect Interrupt Status 2 (Buttons) + - cs42l42 documents a 0x130a as Detect Interrupt Status 2 + no such register ever used on OSX + assuming this is a difference for the cs42l83 + 0x80 M_DETECT_TRUE_FALSE + 0x40 M_DETECT_FALSE_TRUE + 0x04 M_HSBIAS_HIZ + 0x02 M_SHORT_RELEASE + 0x01 M_SHORT_DETECTED + OSX status values seen + 0x0a after 0xe7 mask set + 0x40 pre configure button response mask + 0x04 pre disable button interrupts + 0x1b78 Detect Status 2 - seems to be associated with buttons and/or mike + 0x02 HS_TRUE - cs42l42 + 0x01 SHORT_TRUE - cs42l42 + OSX values seen + 0x40 button detect + 0x02 mike sense + new values seen for imacs with non-Apple headsets + 0x20 button detect?? + 0x131b Codec Interrupt Mask + 0x02 M_HSDET_AUTO_DONE + 0x01 M_PDN_DONE + 0x1308 Codec Interrupt Status + 0x02 M_HSDET_AUTO_DONE + 0x01 M_PDN_DONE + 0x1320 Tip/Ring Sense Plug/Unplug Interrupt Mask + 0x08 M_TS_UNPLUG + 0x04 M_TS_PLUG + 0x02 M_RS_UNPLUG + 0x01 M_RS_PLUG + 0x130f Tip/Ring Sense Plug/Unplug Interrupt Status + 0x08 M_TS_UNPLUG + 0x04 M_TS_PLUG + 0x02 M_RS_UNPLUG + 0x01 M_RS_PLUG + + register 0x1b7b: + + TIP_SENSE_PLUG 0x40 + TIP_SENSE_UNPLUG 0x20 + + BUTTON_DOWN_PRESS 0x1 + BUTTON_UP_PRESS 0x2 + BUTTON_RELEASE 0x10 + + BUTTONS (BUTTON_UP_PRESS | BUTTON_DOWN_PRESS) + + register 0x1b7c: + + BUTTON_DETECT_MAIN 0x18 // we only see 0x08 but the mask allows for these 2 bits + + register 0x1b78: + + BUTTON_DETECT_MASK 0x60 // now seen 2 bits used - plausibly depends if Apple with buttons (0x40) or non-Apple with buttons (0x20) + BUTTON_DETECT1 0x40 + BUTTON_DETECT2 0x20 + MIKE_CONNECT 0x02 + + register 0x131b: + + HSDET_AUTO_DONE 0x02 + PDN_DONE 0x01 + + + */ + + + +/* + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + +struct unsol_item { + struct list_head list; + unsigned int idx; + unsigned int res; +}; + +struct hda_cvt_setup_apple { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; + +struct cs8409_apple_spec { + struct hda_gen_spec gen; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ + unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* digital beep */ + hda_nid_t beep_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + + // so it appears we have "concurrency" in the linux HDA code + // in that if unsolicited responses occur which perform extensive verbs + // the hda verbs are intermixed with eg extensive start playback verbs + // on OSX we appear to have blocks of verbs during which unsolicited responses + // are logged but the unsolicited verbs occur after the verb block + // this flag is used to flag such verb blocks and the list will store the + // responses + // we use a pre-allocated list - if we have more than 10 outstanding unsols + // we will drop + // not clear if mutexes would be the way to go + int block_unsol; + struct list_head unsol_list; + struct unsol_item unsol_items_prealloc[10]; + int unsol_items_prealloc_used[10]; + + // add in specific nids for the intmike and linein as they seem to swap + // between macbook pros (14,3) and imacs (18,3) + int intmike_nid; + int linein_nid; + int intmike_adc_nid; + int linein_amp_nid; + + // the following flag bits also need swapping + int reg9_intmike_dmic_mo; + int reg9_linein_dmic_mo; + int reg82_intmike_dmic_scl; + int reg82_linein_dmic_scl; + + + // add explicit stream format store entries as per hda_codec using a local definition + // of hda_cvt_setup (which is local to hda_codec.c) + // also use explicit nid versions + // (except that means either need explicit functions for each nid or have to lookup + // nid each time want to use in a generic function with nid argument) + struct hda_cvt_setup_apple nid_0x02; + struct hda_cvt_setup_apple nid_0x03; + struct hda_cvt_setup_apple nid_0x0a; + struct hda_cvt_setup_apple nid_0x22; + struct hda_cvt_setup_apple nid_0x23; + struct hda_cvt_setup_apple nid_0x1a; + + + // new item to deal with jack presence as Apple seems to have barfed + // the HDA spec by using a separate headphone chip + int jack_present; + + // save the type of headphone connected + int headset_type; + + // if headphone has mike or not + int have_mike; + + // if headphone has buttons or not + int have_buttons; + + // current stream channel count + int stream_channels; + + // set when playing for plug/unplug events while playing + int playing; + + // set when capturing for plug/unplug events while capturing + int capturing; + + // changing coding - OSX sets up the format on plugin + // then does some minimal setup when start play + // initial coding delayed any format setup till actually play + // this works for no mike but not for mike - we need to initialize + // the mike on plugin + // this flag will be set when we have done the format setup + // so know if need to do it on play or not + // now need 2 flags - one for play and one for capture + int headset_play_format_setup_needed; + int headset_capture_format_setup_needed; + + int headset_presetup_done; + + + int use_data; + + + // this is new item for dealing with headset plugins + // so can distinguish which phase we are in if have multiple interrupts + // now primarily used to indicate if booted with headset plugged in + int headset_phase; + + // another dirty hack item to manage the different headset enable codes + int headset_enable; + + int play_init; + int capture_init; + + // new item to limit times we redo unmute/play +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 last_play_time; +#else + struct timespec last_play_time; +#endif + // record the first play time - we have a problem there + // some initial plays that I dont understand - so skip any setup + // till sometime after the first play +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 first_play_time; +#else + struct timespec first_play_time; +#endif + +}; +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +#define cs8409_apple_spec cs8409_spec +#endif + + +// coding copied from hda_generic.c to print the nid path details + + +#define debug_badness(fmt, ...) \ + mycodec_dbg(codec, fmt, ##__VA_ARGS__) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path) +{ + char buf[40]; + char *pos = buf; + int i; + + *pos = 0; + for (i = 0; i < path->depth; i++) + pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", + pos != buf ? ":" : "", + path->path[i]); + + mycodec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); +} + +#else + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path); + +#endif + + +static inline void print_nid_path_idx(struct hda_codec *codec, + const char *pfx, int idx) +{ + struct nid_path *path; + + path = snd_hda_get_path_from_idx(codec, idx); + if (path) + print_nid_path(codec, pfx, path); +} + +static void debug_show_configs(struct hda_codec *codec, + struct auto_pin_cfg *cfg) +{ + struct hda_gen_spec *spec = codec->spec; + static const char * const lo_type[3] = { "LO", "SP", "HP" }; + int i; + + debug_badness("debug_show_configs start\n"); + + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[3], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3], + lo_type[cfg->line_out_type]); + for (i = 0; i < cfg->line_outs; i++) + print_nid_path_idx(codec, " out", spec->out_paths[i]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + for (i = 0; i < spec->multi_ios; i++) + print_nid_path_idx(codec, " mio", + spec->out_paths[cfg->line_outs + i]); + if (cfg->hp_outs) + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[3], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + for (i = 0; i < cfg->hp_outs; i++) + print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); + if (cfg->speaker_outs) + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); + for (i = 0; i < cfg->speaker_outs; i++) + print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); + for (i = 0; i < 3; i++) + print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); + debug_badness("debug_show_configs end\n"); +} + +// attempt at an explicit setup ie not generic +//#include "patch_cirrus_explicit.h" + + +static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action); + +// this is a copy from playback_pcm_prepare in hda_generic.c +// initially I needed to do the Apple setup BEFORE the snd_hda_multi_out_analog_prepare +// in order to overwrite the Apple setup with the actual format/stream id +// NOTA BENE - if playback_pcm_prepare is changed in hda_generic.c then +// those changes must be re-implemented here +// we need this order because snd_hda_multi_out_analog_prepare writes the +// the format and stream id's to the audio nodes +//// so far we have left the Apple setup of the nodes format and stream id's in +// now updated to set the actual format where Apple does the format/stream id setup +// Apples format is very specifically S24_3LE (24 bit), 4 channel, 44.1 kHz +// S24_3LE seems to be very difficult to create so best Ive done is +// S24_LE (24 in 32 bits) or S32_LE +// it seems the digital setup is able to handle this with the Apple TDM +// setup but if we use the normal prepare hook order this overrwites +// the node linux 0x2, 0x3 setup with the Apple setup which leads to noise +// (the HDA specs say the node format setup must match the data) +// if we do the Apple setup and then the snd_hda_multi_out_analog_prepare +// the nodes will have the slightly different but working format +// with proper update of stream format at same point as in Apple log we need to pass +// the actual playback format as passed to this routine to our new "hook" +// cs_8409_pcm_playback_pre_prepare_hook +// to define the cached format correctly in that routine +// so far my analysis is that hinfo stores the stream format in the kernel format style +// but what is passed to cs_8409_playback_pcm_prepare is the format in HDA style +// not yet figured how to convert from kernel format style to HDA style + +static int cs_8409_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + int err; + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare\n"); + + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n", + hinfo->nid, stream_tag, format); + + cs_8409_pcm_playback_pre_prepare_hook(hinfo, codec, stream_tag, format, substream, + HDA_GEN_PCM_ACT_PREPARE); + + // now dont think we need this - we now explicitly copy the 1st 2 channels to 2nd 2 channels + // if given 2 channel input + // plus all headset output is also explicitly being done + // well thats unexpected - if we comment this we loose headphone output + //err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + // stream_tag, format, substream); + err = 0; + + // we cant call directly as call_pcm_playback_hook is local to hda_generic.c + //if (!err) + // call_pcm_playback_hook(hinfo, codec, substream, + // HDA_GEN_PCM_ACT_PREPARE); + // but its a trivial function - at least for the moment!! + if (err) + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare err %d\n", err); + if (!err) + if (spec->pcm_playback_hook) + spec->pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); + return err; +} + + +static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action); + + +// this is a copy from capture_pcm_prepare in hda_generic.c +// NOTA BENE - if capture_pcm_prepare is changed in hda_generic.c then +// those changes must be re-implemented here +static int cs_8409_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_dbg(codec, "cs_8409_capture_pcm_prepare\n"); + + mycodec_dbg(codec, "cs_8409_capture_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n", + hinfo->nid, stream_tag, format); + + + cs_8409_pcm_capture_pre_prepare_hook(hinfo, codec, stream_tag, format, substream, + HDA_GEN_PCM_ACT_PREPARE); + + // we have a problem - this has to handle 2 different types of stream - the internal mike + // and the external headset mike (cs42l83) + + + // NOTE - the following snd_hda_codec_stream no longer do anything + // we have already set the stream data in the pre prepare hook + // - so as the format here is same (or at least should be!!) as that setup there is no format difference to that + // cached and snd_hda_coded_setup_stream does nothing + + if (hinfo->nid == spec->intmike_adc_nid) + { + + // so this is getting stranger and stranger + // the most valid recording is S24_3LE (0x4031) - except that the data we get out is S32_LE (low byte 0) + // - so it doesnt play right - and it messes with arecords vumeter + // (S32_LE is officially 0x4041 - but using that format doesnt seem to have valid data - audio very low) + //// so now try forcing the format here to 0x4031 + //// well that fails miserably - the format mismatch stops data totally + // it now appears we get the same data with either 0x4031 or 0x4041 - both are low volume + // - however scaling (normalizing) in audacity we get the right sound with similar quality to OSX + // so now think the low volume is right - and OSX must be scaling/processing the data in CoreAudio + // (is the internal mike a fake 24 bits - ie its actually 16 bits but stuffed in the low end of the + // 24 bits - hence low volume - preliminary scaling attempts in audacity suggest this might be true!!) + + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + + } + else if (hinfo->nid == 0x1a) + { + + // do we need a pre-prepare function?? + // maybe for this the external mike ie cs42l83 input + + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + + } + else + dev_info(hda_codec_dev(codec), "cs_8409_capture_pcm_prepare - UNIMPLEMENTED input nid 0x%x\n",hinfo->nid); + + // we cant call directly as call_pcm_capture_hook is local to hda_generic.c + //call_pcm_capture_hook(hinfo, codec, substream, + // HDA_GEN_PCM_ACT_PREPARE); + // but its a trivial function - at least for the moment!! + // note this hook if defined also needs to switch between the 2 versions of input!! + if (spec->gen.pcm_capture_hook) + spec->gen.pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); + + return 0; +} + + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_tbl * +cs8409_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; +} + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_tbl * +cs_8409_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_mst(codec, nid, dev_id); +#else + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); +#endif + struct hda_jack_tbl *existing_nid_jack = cs8409_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; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + jack->dev_id = dev_id; +#endif + 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; + } + //// use this to prevent AC_VERB_GET_PIN_SENSE (f09) verbs being sent - not seen in OSX logs + // well it does but it also makes the jack seem permanently connected + // now going with Dell style fixup + //jack->phantom_jack = 1; + return jack; +} + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_callback * +cs_8409_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; +} + +// quick debug callback list function +void cs_8409_dump_callback(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + struct hda_jack_callback *cb; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + for (cb = jack->callback; cb; cb = cb->next) { + printk("snd_hda_intel: cs_8409_dump_callback jack num %d nid 0x%02x tag 0x%08x func %pF\n",i,jack->nid,jack->tag,cb->func); + } + } + + return; +} + +// copy of snd_hda_jack_detect_enable_callback code so doesnt send AC_VERB_SET_UNSOLICITED_ENABLE +// for Apple there are no AC_VERB_SET_UNSOLICITED_ENABLE verbs sent for 8409 +// it appears unsolicited response is pre-enabled +// but we need to fix this to setup the callback on such responses +// note that the current (>5.13) callback does not have a tag argument +struct hda_jack_callback * +cs_8409_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, int dev_id, int tag, + hda_jack_callback_fn func) +{ + struct hda_jack_tbl *jack; + struct hda_jack_callback *callback = NULL; + int err; + + myprintk("snd_hda_intel: cs_8409_hda_jack_detect_enable_callback nid 0x%02x dev_id %d tag 0x%02x\n", nid, dev_id, tag); + + jack = cs_8409_hda_jack_tbl_new(codec, nid, dev_id); + if (!jack) + return ERR_PTR(-ENOMEM); + + callback = cs_8409_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; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + callback->dev_id = jack->dev_id; +#endif + callback->next = jack->callback; + jack->callback = callback; + } + + if (jack->jack_detect) + return callback; /* already registered */ + jack->jack_detect = 1; + // update the tag - linux code just counted the number of jacks set up + // for a tag - now done in cs_8409_hda_jack_tbl_new + // DANGEROUS - we do NOT check for unique tag here!! + if (tag != 0) + jack->tag = tag; + if (codec->jackpoll_interval > 0) + return callback; /* No unsol if we're polling instead */ + // apparently we dont need to send this + //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; +} + +// this is a copy of local routine call_jack_callback from hda_jack.c +static void cs_8409_apple_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 = +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, jack->dev_id); +#else + snd_hda_jack_tbl_get(codec, jack->gated_jack); +#endif + if (gated) { + for (cb = gated->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; + cb->func(codec, cb); + } + } + } +} + + +#ifdef APPLE_CHANNEL_MAP +// new attempt to solve the channel map issue +static const struct snd_pcm_chmap_elem cs_8409_chmap[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_RL, + SNDRV_CHMAP_FR, SNDRV_CHMAP_RR } }, + { } +}; + +// this adds controls which I have no idea what they do +static void cs_8409_add_chmap_ctls(struct hda_codec *codec) +{ + int err = 0; + struct hda_pcm *pcm; + + mycodec_dbg(codec, "cs_8409_add_chmap_ctls enter"); + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct hda_pcm_stream *hinfo = + &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + struct snd_pcm_chmap *chmap; + const struct snd_pcm_chmap_elem *elem; + + if (pcm != NULL) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm name %s", pcm->name); + + if (hinfo != NULL) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo OK"); + elem = cs_8409_chmap; + if (hinfo->channels_max == 4) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo chan == 4"); + err = snd_pcm_add_chmap_ctls(pcm->pcm, + SNDRV_PCM_STREAM_PLAYBACK, + elem, hinfo->channels_max, 0, &chmap); + if (err < 0) + mycodec_dbg(codec, "cs_8409_add_chmap_ctls failed!"); + } + } else { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo NULL"); + } + } else { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm NULL"); + } + } + + mycodec_dbg(codec, "cs_8409_add_chmap_ctls exit"); +} +#endif + +#ifdef ADD_EXTENDED_VERB +static void cs_8409_set_extended_codec_verb(void); +#endif + + +static void cs_8409_dump_auto_config(struct hda_codec *codec, const char *label_string) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + int itm = 0; + + myprintk("snd_hda_intel: dump %s\n", label_string); + + // dump headphone config + myprintk("snd_hda_intel: headphone config hp_jack_present %d\n",spec->gen.hp_jack_present); + myprintk("snd_hda_intel: headphone config line_jack_present %d\n",spec->gen.line_jack_present); + myprintk("snd_hda_intel: headphone config speaker_muted %d\n",spec->gen.speaker_muted); + myprintk("snd_hda_intel: headphone config line_out_muted %d\n",spec->gen.line_out_muted); + myprintk("snd_hda_intel: headphone config auto_mic %d\n",spec->gen.auto_mic); + myprintk("snd_hda_intel: headphone config automute_speaker %d\n",spec->gen.automute_speaker); + myprintk("snd_hda_intel: headphone config automute_lo %d\n",spec->gen.automute_lo); + myprintk("snd_hda_intel: headphone config detect_hp %d\n",spec->gen.detect_hp); + myprintk("snd_hda_intel: headphone config detect_lo %d\n",spec->gen.detect_lo); + myprintk("snd_hda_intel: headphone config keep_vref_in_automute %d\n",spec->gen.keep_vref_in_automute); + myprintk("snd_hda_intel: headphone config line_in_auto_switch %d\n",spec->gen.line_in_auto_switch); + myprintk("snd_hda_intel: headphone config auto_mute_via_amp %d\n",spec->gen.auto_mute_via_amp); + myprintk("snd_hda_intel: headphone config suppress_auto_mute %d\n",spec->gen.suppress_auto_mute); + myprintk("snd_hda_intel: headphone config suppress_auto_mic %d\n",spec->gen.suppress_auto_mic); + + myprintk("snd_hda_intel: headphone config hp_mic %d\n",spec->gen.hp_mic); + + myprintk("snd_hda_intel: headphone config suppress_hp_mic_detect %d\n",spec->gen.suppress_hp_mic_detect); + + + myprintk("snd_hda_intel: auto config pins line_outs %d\n", spec->gen.autocfg.line_outs); + myprintk("snd_hda_intel: auto config pins line_outs 0x%02x\n", spec->gen.autocfg.line_out_pins[0]); + myprintk("snd_hda_intel: auto config pins line_outs 0x%02x\n", spec->gen.autocfg.line_out_pins[1]); + myprintk("snd_hda_intel: auto config pins speaker_outs %d\n", spec->gen.autocfg.speaker_outs); + myprintk("snd_hda_intel: auto config pins speaker_outs 0x%02x\n", spec->gen.autocfg.speaker_pins[0]); + myprintk("snd_hda_intel: auto config pins speaker_outs 0x%02x\n", spec->gen.autocfg.speaker_pins[1]); + myprintk("snd_hda_intel: auto config pins hp_outs %d\n", spec->gen.autocfg.hp_outs); + myprintk("snd_hda_intel: auto config pins hp_outs 0x%02x\n", spec->gen.autocfg.hp_pins[0]); + myprintk("snd_hda_intel: auto config pins inputs %d\n", spec->gen.autocfg.num_inputs); + + for (itm = 0; itm < spec->gen.autocfg.num_inputs; itm++) { + + myprintk("snd_hda_intel: auto config pins inputs pin 0x%02x\n", spec->gen.autocfg.inputs[itm].pin); + myprintk("snd_hda_intel: auto config pins inputs type %d\n", spec->gen.autocfg.inputs[itm].type); + myprintk("snd_hda_intel: auto config pins inputs is head set mic %d\n", spec->gen.autocfg.inputs[itm].is_headset_mic); + myprintk("snd_hda_intel: auto config pins inputs is head phn mic %d\n", spec->gen.autocfg.inputs[itm].is_headphone_mic); + myprintk("snd_hda_intel: auto config pins inputs is boost %d\n", spec->gen.autocfg.inputs[itm].has_boost_on_pin); + } + + myprintk("snd_hda_intel: auto config inputs num_adc_nids %d\n", spec->gen.num_adc_nids); + for (itm = 0; itm < spec->gen.num_adc_nids; itm++) { + myprintk("snd_hda_intel: auto config inputs adc_nids 0x%02x\n", spec->gen.adc_nids[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is num_dacs %d\n", spec->gen.multiout.num_dacs); + for (itm = 0; itm < spec->gen.multiout.num_dacs; itm++) { + myprintk("snd_hda_intel: auto config multiout is dac_nids 0x%02x\n", spec->gen.multiout.dac_nids[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is hp_nid 0x%02x\n", spec->gen.multiout.hp_nid); + + for (itm = 0; itm < ARRAY_SIZE(spec->gen.multiout.hp_out_nid); itm++) { + if (spec->gen.multiout.hp_out_nid[itm]) + myprintk("snd_hda_intel: auto config multiout is hp_out_nid 0x%02x\n", spec->gen.multiout.hp_out_nid[itm]); + } + for (itm = 0; itm < ARRAY_SIZE(spec->gen.multiout.extra_out_nid); itm++) { + if (spec->gen.multiout.extra_out_nid[itm]) + myprintk("snd_hda_intel: auto config multiout is xtr_out_nid 0x%02x\n", spec->gen.multiout.extra_out_nid[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is dig_out_nid 0x%02x\n", spec->gen.multiout.dig_out_nid); + myprintk("snd_hda_intel: auto config multiout is slv_dig_out %p\n", spec->gen.multiout.follower_dig_outs); + +} + + +static void cs_8409_dump_path(struct nid_path *path) +{ + int i; + int j; + + myprintk("snd_hda_intel: dump path\n"); + + myprintk("snd_hda_intel: path %d: active %d pin_enabled %d pin_fixed %d stream_enabled %d\n", i, path->active, path->pin_enabled, path->pin_fixed, path->stream_enabled); + myprintk("snd_hda_intel: path %d: depth %d\n", i, path->depth); + for(j=0; j < path->depth; j++) { + myprintk("snd_hda_intel: path %d: path nid 0x%02x idx %d multi %d\n", j, path->path[j], path->idx[j], path->multi[j]); + } + for(j=0; j < NID_PATH_NUM_CTLS; j++) { + myprintk("snd_hda_intel: ctl %d: path 0x%08x\n", j, path->ctls[j]); + } + + myprintk("snd_hda_intel: dump path end\n"); +} + + +// so now think multi in the path is different from multiout +// - now think its about if there are multiple connections as listed by AC_VERB_GET_CONNECT_LIST +// - still havent figured out idx tho + +static void cs_8409_dump_paths(struct hda_codec *codec, const char *label_string) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + const struct nid_path *path; + int i; + int j; + + myprintk("snd_hda_intel: dump paths %s\n", label_string); + + snd_array_for_each(&spec->gen.paths, i, path) { + myprintk("snd_hda_intel: path %d: active %d pin_enabled %d pin_fixed %d stream_enabled %d\n", i, path->active, path->pin_enabled, path->pin_fixed, path->stream_enabled); + myprintk("snd_hda_intel: path %d: depth %d\n", i, path->depth); + for(j=0; j < path->depth; j++) { + myprintk("snd_hda_intel: path %d: path nid 0x%02x idx %d multi %d\n", j, path->path[j], path->idx[j], path->multi[j]); + } + for(j=0; j < NID_PATH_NUM_CTLS; j++) { + myprintk("snd_hda_intel: ctl %d: path 0x%08x\n", j, path->ctls[j]); + } + + } + + myprintk("snd_hda_intel: dump paths end\n"); +} + + + +// renamed this function as we dont want it called on resume +// because we are using an explicit version of build controls we can add it there + +static int cs_8409_apple_boot_init(struct hda_codec *codec) +{ + struct hda_pcm *info = NULL; + struct hda_pcm_stream *hinfo = NULL; + struct cs8409_apple_spec *spec = NULL; + //struct snd_kcontrol *kctl = NULL; + int pcmcnt = 0; + int ret_unsol_enable = 0; + + // so apparently if we do not define a resume function + // then this init function will be called on resume + // is that what we want here?? + // NOTE this is called for either playback or capture + + myprintk("snd_hda_intel: cs_8409_apple_boot_init\n"); + + //dump_stack(); + + + // NOTE that this function is called after the build pcm functions + + + // dump the rates/format of the afg node + // so analog_playback_stream is still NULL here - maybe only defined when doing actual playback + // the info stream is now defined + spec = codec->spec; + hinfo = spec->gen.stream_analog_playback; + if (hinfo != NULL) + { + mycodec_dbg(codec, "hinfo stream nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + } + else + mycodec_dbg(codec, "hinfo stream NULL\n"); + + // think this is what I need to fixup + + list_for_each_entry(info, &codec->pcm_list_head, list) { + int stream; + + mycodec_dbg(codec, "cs_8409_apple_boot_init pcm %d\n",pcmcnt); + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *hinfo = &info->stream[stream]; + + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d pointer %p\n",stream,hinfo); + + if (hinfo != NULL) + { + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d nid 0x%02x rates 0x%08x formats 0x%016llx\n",stream,hinfo->nid,hinfo->rates,hinfo->formats); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream substreams %d\n",hinfo->substreams); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream channels min %d\n",hinfo->channels_min); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream channels max %d\n",hinfo->channels_max); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream maxbps %d\n",hinfo->maxbps); + } + else + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d NULL\n", stream); + } + pcmcnt++; + } + + // update the streams specifically by nid + // we seem to have only 1 stream here with the nid of 0x02 + // (I still dont really understand the linux generic coding here) + // with capture devices we seem to get 2 pcm streams (0 and 1) + // each pcm stream has an output stream (0) and an input stream (1) + // the 1st pcm stream (0) is assigned nid 0x02 for output and nid 0x22 (macbook pro) for input (internal mike) + // the 2nd pcm stream (1) has a dummy output stream and nid 0x1a for input (headset mike via cs42l83) + // (NOTE this means the line input stream (0x45->0x23) (macbook pro) is not assigned currently ie not useable) + + list_for_each_entry(info, &codec->pcm_list_head, list) { + int stream; + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *hinfo = &info->stream[stream]; + + if (hinfo != NULL) + { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + { + if (hinfo->nid == 0x02) + { + mycodec_dbg(codec, "cs_8409_apple_boot_init info playback stream %d pointer %p\n",stream,hinfo); + // so now we need to force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // probably can leave S32_LE + // we can still handle 2/4 channel (what about 1 channel?) + hinfo->rates = SNDRV_PCM_RATE_44100; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE; + mycodec_dbg(codec, "playback info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + + // update the playback function + hinfo->ops.prepare = cs_8409_playback_pcm_prepare; + } + } + else if (stream == SNDRV_PCM_STREAM_CAPTURE) + { + //if (hinfo->nid == 0x22) + if (hinfo->nid == spec->intmike_adc_nid) + { + // this is the internal mike + // this is a bit weird - the output nodes are id'ed by input pin nid + // but the input nodes are done by the input (adc) nid - not the input pin nid + mycodec_dbg(codec, "cs_8409_apple_boot_init info capture stream %d pointer %p\n",stream,hinfo); + // so now we could force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // but this internal mike seems to be a standard HDA input setup so we could have any format here + //hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE; + hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S24_3LE; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE; + //hinfo->maxbps = 24; + mycodec_dbg(codec, "capture info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx maxbps %d\n",hinfo->nid,hinfo->rates,hinfo->formats,hinfo->maxbps); + // update the capture function + hinfo->ops.prepare = cs_8409_capture_pcm_prepare; + } + else if (hinfo->nid == 0x1a) + { + // this is the external mike ie headset mike + // this is a bit weird - the output nodes are id'ed by input pin nid + // but the input nodes are done by the input (adc) nid - not the input pin nid + mycodec_dbg(codec, "cs_8409_apple_boot_init info capture stream %d pointer %p\n",stream,hinfo); + // so now we force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // - because this format is the one being returned by the cs42l83 which is setup by undocumented i2c commands + hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S24_LE; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE; + //hinfo->maxbps = 24; + mycodec_dbg(codec, "capture info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx maxbps %d\n",hinfo->nid,hinfo->rates,hinfo->formats,hinfo->maxbps); + // update the capture function + hinfo->ops.prepare = cs_8409_capture_pcm_prepare; + } + // still not sure what we do about the linein nid + // is this bidirectional - because we have no lineout as far as I can see + } + } + else + mycodec_dbg(codec, "cs_8409_apple_boot_init info pcm stream %d NULL\n", stream); + } + pcmcnt++; + } + + + //list_for_each_entry(kctl, &codec->card->controls, list) { + //} + + +#ifdef ADD_EXTENDED_VERB + cs_8409_set_extended_codec_verb(); +#endif + + myprintk("snd_hda_intel: end cs_8409_apple_boot_init\n"); + + return 0; +} + + +static int cs_8409_apple_init(struct hda_codec *codec) +{ + struct hda_pcm *info = NULL; + struct hda_pcm_stream *hinfo = NULL; + struct cs8409_apple_spec *spec = NULL; + //struct snd_kcontrol *kctl = NULL; + int pcmcnt = 0; + int ret_unsol_enable = 0; + + // not sure what the init function is supposed to be doing + // its called in snd_hda_codec_build_controls + // but also from the default resume function hda_call_codec_resume + + // so apparently if we do not define a resume function + // then this init function will be called on resume + // is that what we want here?? + // NOTE this is called for either playback or capture + + myprintk("snd_hda_intel: cs_8409_apple_init\n"); + + //dump_stack(); + + + //if (spec->vendor_nid == CS420X_VENDOR_NID) { + // /* init_verb sequence for C0/C1/C2 errata*/ + // snd_hda_sequence_write(codec, cs_errata_init_verbs); + // snd_hda_sequence_write(codec, cs_coef_init_verbs); + //} else if (spec->vendor_nid == CS4208_VENDOR_NID) { + // snd_hda_sequence_write(codec, cs4208_coef_init_verbs); + //} + + + //// so it looks as tho we have an issue when using headsets + //// - because the 8409 is totally messed up it does not switch the inputs + //// when a headset is plugged in + //// not sure about this here - maybe move to where disable internal mike nodes + //if (spec->jack_present) { + //} + + + // so the following powers on all active nodes - but if we have just plugged + // in a headset thats still the internal mike and amps + + // commenting this as we dont need it + // it powers on the nids, plus updates the mute/volume + // via path_power_update and activate_amp_in or activate_amp_out + //snd_hda_gen_init(codec); + + + // read UNSOL enable data to see what initial setup is + //ret_unsol_enable = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x01 boot setup is 0x%08x\n",ret_unsol_enable); + //ret_unsol_enable = snd_hda_codec_read(codec, 0x47, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x47 boot setup is 0x%08x\n",ret_unsol_enable); + + + //if (spec->gpio_mask) { + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + // spec->gpio_mask); + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + // spec->gpio_dir); + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + // spec->gpio_data); + //} + + //if (spec->vendor_nid == CS420X_VENDOR_NID) { + // init_input_coef(codec); + // init_digital_coef(codec); + //} + + + myprintk("snd_hda_intel: end cs_8409_apple_init\n"); + + return 0; +} + +static int cs_8409_apple_resume(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + myprintk("snd_hda_intel: cs_8409_apple_resume\n"); + // code copied from default resume patch ops + if (spec && spec->vops && spec->vops->init) + spec->vops->init(codec); + snd_hda_regmap_sync(codec); + myprintk("snd_hda_intel: end cs_8409_apple_resume\n"); + return 0; +} + +static int cs_8409_apple_suspend(struct hda_codec *codec) +{ + myprintk("snd_hda_intel: cs_8409_apple_suspend\n"); + // no additional code for this so just dummy it + return 0; +} + +static int cs_8409_apple_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + myprintk("snd_hda_intel: cs_8409_apple_check_power_status\n"); + // no additional code for this so just dummy it + return 0; +} + + +static int cs_8409_apple_build_controls(struct hda_codec *codec) +{ + int err; + + myprintk("snd_hda_intel: cs_8409_apple_build_controls\n"); + //dump_stack(); + + + + // try here - its mainly dumping the pcms which have been built by now + cs_8409_apple_boot_init(codec); + + // so if we have paths (active??) then snd_hda_gen_build_controls will add the end point + // (ie adc input nid or output nid (often dac but just pass through for 8409) as jacks + // if the nid has no PIN_SENSE then these will be phantom jacks (which means treated as always plugged in) + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + + // this seems better as snd_hda_gen_build_controls does an hda_exec_init_verbs call + // before calling the init patch_ops function + // - on the other hand currently it is just dumping data doesnt actually do anything + //cs_8409_apple_boot_init(codec); + + + // before or after the snd_hda_apply_fixup?? + //cs_8409_add_chmap_ctls(codec); + + myprintk("snd_hda_intel: end cs_8409_apple_build_controls\n"); + return 0; +} + +int cs_8409_apple_build_pcms(struct hda_codec *codec) +{ + int retval; + struct hda_pcm *pcm; + //struct cs8409_apple_spec *spec = codec->spec; + //struct hda_pcm *info = NULL; + //struct hda_pcm_stream *hinfo = NULL; + + + myprintk("snd_hda_intel: cs_8409_apple_build_pcms\n"); + + + // so I dont get how this leads to the observed controls in pulse/alsa + // so far snd_hda_gen_build_pcms seems to add channel maps of stereo and 2.1 + // - cant figure out where the surround is generated from + // (also cant see any way to disable the 2.1 without explicit recoding) + + + // to replace this with explicit code would require a lot of static function copying + // from hda_generic.c (and maybe hda_codec.c) + // current issue is the default code will add a 2.1 chmap for 4 channels - I want to remove that + retval = snd_hda_gen_build_pcms(codec); + + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct snd_pcm_chmap *chmap; + //const struct snd_pcm_chmap_elem *elem; + if (pcm != NULL) { + myprintk("snd_hda_intel: cs_8409_apple_build_pcms name %s\n", pcm->name); + if (pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap != NULL) { + const struct snd_pcm_chmap_elem *elem; + elem = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap[0]; + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap chnls %d map %d %d\n", elem->channels, elem->map[0], elem->map[1]); + elem = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap[1]; + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap chnls %d map %d %d %d %d\n", elem->channels, elem->map[0], elem->map[1], elem->map[2], elem->map[3]); + } else { + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap NULL\n"); + } + // if (pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max == 4) { + // myprintk("snd_hda_intel: cs_8409_apple_build_pcms - adding chmap OK\n"); + // pcm->own_chmap = true; + // pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = cs_8409_chmap; + // } + } + } + + // we still dont have the pcm streams defined by here + // ah this is all done in snd_hda_codec_build_pcms + // which calls this patch routine or snd_hda_gen_build_pcms + // but the query supported pcms is only done after this + myprintk("snd_hda_intel: end cs_8409_apple_build_pcms\n"); + return retval; +} + + +// copy of cs8409_fix_caps with debug prints +// - rather than adding the prints to the patch_cs8409.c routine +static void cs8409_fix_caps_debug(struct hda_codec *codec, unsigned int nid) +{ + int caps; + + /* CS8409 is simple HDA bridge and intended to be used with a remote + * companion codec. Most of input/output PIN(s) have only basic + * capabilities. Receive and Transmit NID(s) have only OUTC and INC + * capabilities and no presence detect capable (PDC) and call to + * snd_hda_gen_build_controls() will mark them as non detectable + * phantom jacks. However, a companion codec may be + * connected to these pins which supports jack detect + * capabilities. We have to override pin capabilities, + * otherwise they will not be created as input devices. + */ + caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); + if (caps >= 0) + snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, + (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); + + myprintk("snd_hda_intel: cs8409_fix_caps nid 0x%02x pcap 0x%08x : 0x%08x 0x%08x 0x%08x\n",nid,caps,AC_PINCAP_IMP_SENSE,AC_PINCAP_PRES_DETECT,AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT); + myprintk("snd_hda_intel: cs8409_fix_caps nid 0x%02x wcap 0x%08x : 0x%08x\n",nid,get_wcaps(codec, nid),AC_WCAP_UNSOL_CAP); + + snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); +} + + + +// set up some local routines we can call in our main code to call the hda functions we need + +void cs_8409_cs42l83_mark_jack(struct hda_codec *codec) +{ + struct hda_jack_tbl *event; + + mycodec_info(codec, "cs_8409_cs42l83_mark_jack\n"); + + // this is how the Dell fixups do it + // first explicitly get the jacktbl entry using the known jack nids + // so can create the res data tag to pass to the standard snd_hda_jack_unsol_event + // (minus actual GPIO etc data usually seen in the res) + // - which then scans the jacktbl for the tag etc + // but primarily marks the jack as dirty prior to snd_hda_jack_report_sync call + // plus calls the jack callbacks + + event = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L83_HP_PIN_NID, 0); + + // note that the only real functions of snd_hda_jack_unsol_event are to call the jack callbacks + // and snd_hda_jack_report_sync + // for Apple we have disabled power on/power off callbacks (power_save_node = 0) + // so now only have call_hp_automute and call_mic_autoswitch to figure out + // - currently call_hp_automute is disabled because of suppress_auto_mute + + if (event) { + // now perform the functions of snd_hda_jack_unsol_event explicitly + //snd_hda_jack_unsol_event(codec, (event->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); + // ignoring key_report_jack at the moment + event->jack_dirty = 1; + //cs_8409_apple_call_jack_callback(codec, res, event); + //snd_hda_jack_report_sync(codec); + } + + // note that the Dell fixups do calls to snd_hda_jack_unsol_event for each jack nid + // so we have multiple calls to snd_hda_jack_report_sync + // so far looks as though this is OK as eventually snd_hda_jack_report_sync calls snd_kctl_jack_report + // to update user side which first checks if stored data is same as updated data and if so does nothing + //snd_hda_jack_report_sync(codec); + + mycodec_info(codec, "end cs_8409_cs42l83_mark_jack\n"); +} + + +void cs_8409_cs42l83_jack_report_sync(struct hda_codec *codec) +{ + mycodec_info(codec, "cs_8409_cs42l83_jack_report_sync\n"); + snd_hda_jack_report_sync(codec); +} + + +// this function is for updating jacks when headphone is plugged in or unplugged +void cs_8409_cs42l83_jack_report_hp_update(struct hda_codec *codec, int plugin) +{ + mycodec_info(codec, "cs_8409_cs42l83_jack_report_hp_update: %s\n", (plugin?"plugin":"unplug")); + + snd_hda_jack_report_sync(codec); +} + + +static void cs_8409_cs42l83_unsol_event_handler(struct hda_codec *codec, unsigned int unsol_res); + +// so I think this is what gets called for any unsolicited event - including jack plug events +// so anything we do to switch amp/headphone should be done from here + +void cs_8409_cs42l83_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct hda_jack_tbl *event; + //int ret_unsol_enable = 0; + //int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f; + int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT; + + // so we have confirmed that these unsol responses are not in linux kernel interrupt state + //if (in_interrupt()) + // mycodec_info(codec, "cs_8409_cs42l83_unsol_event - INTERRUPT\n"); + //else + // mycodec_info(codec, "cs_8409_cs42l83_unsol_event - not interrupt\n"); + + //// read UNSOL enable data to see what current setup is + //ret_unsol_enable = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x01 at unsol is 0x%08x\n",ret_unsol_enable); + //ret_unsol_enable = snd_hda_codec_read(codec, 0x47, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x47 at unsol is 0x%08x\n",ret_unsol_enable); + + // so it seems the low order byte of the res for the 8409 is a copy of the GPIO register state + //// - except that we dont seem to pass this to the callback functions!! - well that was for linux kernel version 4 + + codec_info(codec, "cs_8409_cs42l83_jack_unsol_event UNSOL 0x%08x tag 0x%02x\n",res,tag); + + + // so now think we dont follow snd_hda_jack_unsol_event - that assumes multiple jacks (nids) from which we have + // to find one from the tag - hence the use of the jacbktbl.used count for the tag + // for Apple we have one tag because we have one jack and multiple nids + // hda_generic implements a gated jack which maybe for handling the case of + // 2 nids per jack (one for headphones and one for head mike) + // (see path_realtek.c which seems to use that for hp nids with mic nids) + // for the apple case even more problematic as we have more than 2 nids, one for headphone output, one for + // headset input and one for linein input (currently have no idea how to handle lineout) all in same jack + + // so far it appears Apple enables speakers and disables intmike and linein with no headphone + // intmike only enabled if perform capture + // dont have any linein enable examples + + // now updating as per the Dell module - which looks up each jacktbl entry explicitly + // not sure where to assign the jacktbl/event + // standard snd_hda_jack_unsol_event does the lookup by tag here + // Dell does it in the interrupt handler for the subcodec by reading/writing to the subcodec + // it now seems we have only one jack nid to worry about so could find it here + // currently going with doing it in cs_8409_cs42l83_mark_jack which we will call + // from the appropriate place as called by the cs_8409_cs42l83_unsol_event_handler call chain + + + + // this likely was for kernel version 4 - all kernel 5 versions seem to pass res to call_jack_callback + //// its the callback struct thats passed as an argument to the callback function + //// so stuff the res data in the private_data member which seems to be used for such a purpose + ////event->callback->private_data = res; + + // leave this as is even tho so far have only 1 tag so not really needed + // so could just call the callback routine directly here + // now removing this - the main callbacks are power up, power down and mic_autoswitch + // at the moment we are ignoring any power up/down calls - everything is permanently powered on + // we still have to deal with mic_autoswitch + // we could do this in the new cs_8409_cs42l83_mark_jack function + //cs_8409_apple_call_jack_callback(codec, res, event); + + + cs_8409_cs42l83_unsol_event_handler(codec, res); + + + // we seem to get a weird UNSOL interrupt during initialization - we want to skip the following + if (((struct cs8409_spec*)(codec->spec))->headset_phase == 0) + return; + + // this is the code that generates the AC_VERB_GET_PIN_SENSE (0xf09) verb + // however if we define the jack as a phantom_jack we do not send the AC_VERB_GET_PIN_SENSE (0xf09) verb + + // NOTA BENE - this is a very significant routine which notifies user space of the + // jack plug/unplug event + // however now think we need to move the actual call to when we know we have a jack + // (as per the Dell code to after the cs42l83 headphone sense) + //snd_hda_jack_report_sync(codec); +} + +// Im pretty convinced that Apple uses a timed event from the plugin event +// before performing further setup +// not clear how to set this up in linux +// timer might be way to go but there are some limitations on the timer function +// which is not clear is going to work here +// now think just using msleeps is the way to go - this is similar to code in patch_realtek.c +// for dealing with similar issues +//static struct timer_list cs_8409_hp_timer; + +//static void cs_8409_hp_timer_callback(struct timer_list *tlist) +//{ +// myprintk("snd_hda_intel: cs_8409_hp_timer_callback\n"); +//} + +// have an explict one for 8409 +// cs_free is just a definition +//#define cs_8409_apple_free snd_hda_gen_free + +void cs_8409_apple_free(struct hda_codec *codec) +{ +#if 0 + struct cs8409_spec *spec = codec->spec; + + /* Cancel i2c clock disable timer, and disable clock if left enabled */ + cancel_delayed_work_sync(&spec->i2c_clk_work); + cs8409_disable_i2c_clock(codec); +#endif + + //del_timer(&cs_8409_hp_timer); + + snd_hda_gen_free(codec); +} + + +// note this must come after any function definitions used + +static const struct cs8409_variant_ops cs_8409_apple_vops = { + .build_controls = cs_8409_apple_build_controls, + .build_pcms = cs_8409_apple_build_pcms, + .init = cs_8409_apple_init, + .destroy = cs_8409_apple_free, + .unsol_event = cs_8409_cs42l83_jack_unsol_event, +#ifdef CONFIG_PM + .resume = cs_8409_apple_resume, + .suspend = cs_8409_apple_suspend, + .check_power_status = cs_8409_apple_check_power_status, +#endif +}; + + + +// jack handling analysis +// now it appears that unsolicited events are assumed to be due to jack plug/unplug events +// so the .unsol_event function is the primary handling function for this +// and this does NOT appear to depend on PIN_SENSE capability +// the PIN_SENSE capability seems to be ONLY needed for automute functionality +// this includes enabling/disabling whether we see a Headphone entry in the settings sound dialog + + +static int cs_8409_apple_create_input_ctls(struct hda_codec *codec); + +static void cs_8409_cs42l83_callback(struct hda_codec *codec, struct hda_jack_callback *event); + + +static int cs_8409_apple_parse_auto_config(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + int err; + int i; + + myprintk("snd_hda_intel: cs_8409_apple_parse_auto_config\n"); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + // dump the actual auto config before mess with it + cs_8409_dump_auto_config(codec, "post snd_hda_gen_parse_auto_config"); + + + // try and dump the nid paths to sort out how to set them + debug_show_configs(codec, &spec->gen.autocfg); + + + // dump the raw path storage + cs_8409_dump_paths(codec, "post auto config"); + + + // this would be done in snd_hda_gen_parse_auto_config by check_auto_mute_availability + // (also by pin power setup) + // so now think we should NOT do this - we would need to add this per jack nid but we only + // want to call cs_8409_cs42l83_callback once per unsol event + //cs_8409_hda_jack_detect_enable_callback(codec, 0x01, 0, 0x37, cs_8409_cs42l83_callback); + + // debug dump of codec jack table callback functions +#ifdef MYSOUNDDEBUGFULL + cs_8409_dump_callback(codec); +#else +#ifdef MYSOUNDDEBUG + cs_8409_dump_callback(codec); +#endif +#endif + + // note that create_input_ctls is called towards the end of snd_hda_gen_parse_auto_config + + // it appears the auto config assumes that inputs are connected to ADCs + // (not true for outputs) + + // according to the Dell version the issue is the 8409 nids have no pin sense capabilities + // and they get set to phantom jacks - the Dell version fixes the 8409 inputs/outputs connected to the cs42l42 companion codec + // with PIN SENSE and DETECT capabilities (cs8409_fix_caps) then uses the cs8409_cs42l42_exec_verb replacement + // for the primary spec->exec_verb function to update the returns from the AC_VERB_GET_PIN_SENSE verb for those + // nids with actual state from the companion codec + // note this has no bearing on the auto config - its for handling the unsol events properly + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + // new routine to setup inputs - based on the hda_generic code + cs_8409_apple_create_input_ctls(codec); +#else + // as of 5.13 the definition of AUTO_CFG_MAX_INS has been increased to handle the 8409 + // so the above may not be needed + // (without the above no input pins were recognised at all) + // need to check if fixed other input definitions in cs_8409_apple_create_input_ctls + // and redo the updated input definitions here +#endif + + + // so do I keep this or not?? + /* keep the ADCs powered up when it's dynamically switchable */ + if (spec->gen.dyn_adc_switch) { + unsigned int done = 0; + for (i = 0; i < spec->gen.input_mux.num_items; i++) { + int idx = spec->gen.dyn_adc_idx[i]; + if (done & (1 << idx)) + continue; + snd_hda_gen_fix_pin_power(codec, + spec->gen.adc_nids[idx]); + done |= 1 << idx; + } + } + + myprintk("snd_hda_intel: end cs_8409_apple_parse_auto_config\n"); + + return 0; +} + +// this is only needed if kernel is < 5.13 +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + +// pigs - we need a lot of hda_generic local functions +#include "patch_cirrus_hda_generic_copy.h" + +// so we need to hack this code because we have more adcs than AUTO_CFG_MAX_INS +// adcs (8) - actual number is 18 +// no good way to do this - except to check connection list for each adc and +// see if connected to nid we are looking at +// so define new function + +static int cs_8409_add_adc_nid(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int nums = 0; + int itm = 0; + + myprintk("snd_hda_intel: cs_8409_add_adc_nid pin 0x%x\n",pin); + + for_each_hda_codec_node(nid, codec) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + int fndnid = 0; + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + + //myprintk("snd_hda_intel: cs_8409_add_adc_nid nid 0x%x\n",nid); + + { + const hda_nid_t *connptr = NULL; + int num_conns = snd_hda_get_conn_list(codec, nid, &connptr); + int i; + fndnid = 0; + for (i = 0; i < num_conns; i++) { + //myprintk("snd_hda_intel: cs_8409_add_adc_nid %d 0x%x\n",num_conns,connptr[i]); + if (connptr[i] == pin) { + fndnid = nid; + } + } + } + if (fndnid == 0) + continue; + + // save only 1st one we match + if (spec->num_adc_nids+1 >= max_nums) + break; + adc_nids[spec->num_adc_nids] = nid; + spec->num_adc_nids += 1; + break; + } + + + mycodec_dbg(codec, "snd_hda_intel: cs_8409_add_adc_nid num nids %d\n",nums); + + for (itm = 0; itm < spec->num_adc_nids; itm++) { + myprintk("snd_hda_intel: cs_8409_add_adc_nid 0x%02x\n", spec->adc_nids[itm]); + } + + myprintk("snd_hda_intel: end cs_8409_add_adc_nid\n"); + + return nums; +} + + + +// copied from parse_capture_source in hda_generic.c +// we need this although not changed (apart from printks) because local to hda_generic.c + +/* parse capture source paths from the given pin and create imux items */ +static int cs_8409_parse_capture_source(struct hda_codec *codec, hda_nid_t pin, + int cfg_idx, int num_adcs, + const char *label, int anchor) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int imux_idx = imux->num_items; + bool imux_added = false; + int c; + + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x\n",pin); + + for (c = 0; c < num_adcs; c++) { + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x adc 0x%x check reachable\n",pin,adc); + + if (!is_reachable_path(codec, pin, adc)) + continue; + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x adc 0x%x reachable\n",pin,adc); + path = snd_hda_add_new_path(codec, pin, adc, anchor); + if (!path) + continue; + print_nid_path(codec, "input", path); + spec->input_paths[imux_idx][c] = + snd_hda_get_path_idx(codec, path); + + if (!imux_added) { + if (spec->hp_mic_pin == pin) + spec->hp_mic_mux_idx = imux->num_items; + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); + imux_added = true; + if (spec->dyn_adc_switch) + spec->dyn_adc_idx[imux_idx] = c; + } + } + + myprintk("snd_hda_intel: end cs_8409_parse_capture_source\n"); + + return 0; +} + + +#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ + +// copied from create_input_ctls in hda_generic.c + +static int cs_8409_apple_create_input_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; + int num_adcs = 0; + int i, err; + unsigned int val; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls\n"); + + // we cannot do this + //num_adcs = cs_8409_fill_adc_nids(codec); + //if (num_adcs < 0) + // return 0; + + // clear out the auto config setup + // hope that all_adcs is not different from adc_nids - doesnt seem to be for auto config only + memset(spec->adc_nids, 0, sizeof(spec->adc_nids)); + memset(spec->all_adcs, 0, sizeof(spec->all_adcs)); + spec->num_adc_nids = 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + int fndadc = 0; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d\n",i); + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x\n",i,pin); + + // now scan all nodes for adc nodes and find one connected to this pin + fndadc = cs_8409_add_adc_nid(codec, pin); + if (!fndadc) + continue; + } + + num_adcs = spec->num_adc_nids; + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = spec->num_adc_nids; + memcpy(spec->all_adcs, spec->adc_nids, spec->num_adc_nids* sizeof(hda_nid_t)); + + err = fill_input_pin_labels(codec); + if (err < 0) + return err; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + int fndadc = 0; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d\n",i); + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x\n",i,pin); + + //// now scan the adc nodes and find one connected to this pin + //fndadc = cs_8409_add_adc_nid(codec, pin); + //if (!fndadc) + // continue; + + val = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, pin); + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x val 0x%x\n",i,pin,val); + + if (pin != spec->hp_mic_pin && + !snd_hda_codec_get_pin_target(codec, pin)) + set_pin_target(codec, pin, val, false); + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x val 0x%x mixer 0x%x\n",i,pin,val,mixer); + + if (mixer) { + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, i, pin, + spec->input_labels[i], + spec->input_label_idxs[i], + mixer); + if (err < 0) + return err; + } + } + + // so connections are from the adc nid to the input pin nid + //{ + //const hda_nid_t conn[256]; + //const hda_nid_t *connptr = conn; + //int num_conns = snd_hda_get_conn_list(codec, pin, &connptr); + //int i; + //myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls pin 0x%x num conn %d\n",pin,num_conns); + //for (i = 0; i < num_conns; i++) { + // myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls pin 0x%x conn 0x%x\n",pin,conn[i]); + //} + //} + + + // this is the problem routine - this loops over the adcs to do anything + // so if num_adcs is 0 or none of the adc entries are used this does nothing + + err = cs_8409_parse_capture_source(codec, pin, i, num_adcs, + spec->input_labels[i], -mixer); + if (err < 0) + return err; + + // comment for the moment as needs lots of other functions + //if (spec->add_jack_modes) { + // err = create_in_jack_mode(codec, pin); + // if (err < 0) + // return err; + //} + } + + /* add stereo mix when explicitly enabled via hint */ + if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { + err = cs_8409_parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, + "Stereo Mix", 0); + if (err < 0) + return err; + else + spec->suppress_auto_mic = 1; + } + + myprintk("snd_hda_intel: end cs_8409_apple_create_input_ctls\n"); + + return 0; +} + +#endif + +/* do I need this for 8409 - I certainly need some gpio patching */ +static void cs_8409_apple_fixup_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio\n"); + + // allowable states + // HDA_FIXUP_ACT_PRE_PROBE, + // HDA_FIXUP_ACT_PROBE, + // HDA_FIXUP_ACT_INIT, + // HDA_FIXUP_ACT_BUILD, + // HDA_FIXUP_ACT_FREE, + + // so inspection suggests no eapd usage on macs - no 0xf0c or 0x70c commands sent + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + //struct cs8409_apple_spec *spec = codec->spec; + + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio pre probe\n"); + + //myprintk("fixup gpio hp=0x%x speaker=0x%x\n", hp_out_mask, speaker_out_mask); + //spec->gpio_eapd_hp = hp_out_mask; + //spec->gpio_eapd_speaker = speaker_out_mask; + //spec->gpio_mask = 0xff; + //spec->gpio_data = + // spec->gpio_dir = + // spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } + else if (action == HDA_FIXUP_ACT_PROBE) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio probe\n"); + } + else if (action == HDA_FIXUP_ACT_INIT) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio init\n"); + } + else if (action == HDA_FIXUP_ACT_BUILD) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio build\n"); + } + else if (action == HDA_FIXUP_ACT_FREE) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio free\n"); + } + myprintk("snd_hda_intel: end cs_8409_apple_fixup_gpio\n"); +} + + +// this is from a previous 8409 fixup - remove when see what need to be replaced by + +#ifdef APPLE_FIXUPS + +/* CS8409 */ +enum { + CS8409_MBP131, + CS8409_APPLE_GPIO_0, + CS8409_MBP143, + CS8409_APPLE_GPIO, +}; + +static const struct hda_model_fixup cs8409_apple_models[] = { + { .id = CS8409_MBP131, .name = "mbp131" }, + { .id = CS8409_MBP143, .name = "mbp143" }, + {} +}; + + +static const struct snd_pci_quirk cs8409_apple_fixup_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x3300, "MacBookPro 13,1", CS8409_MBP131), + //SND_PCI_QUIRK(0x106b, 0x3600, "MacBookPro 14,2", CS8409_MBP143), + SND_PCI_QUIRK(0x106b, 0x3900, "MacBookPro 14,3", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x0f00, "Imac 18,2", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x1000, "Imac 18,3", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x1000, "Imac 19,1", CS8409_MBP143), + {} /* terminator */ +}; + +static const struct hda_pintbl mbp131_pincfgs[] = { + {} /* terminator */ +}; + +static const struct hda_pintbl mbp143_pincfgs[] = { + {} /* terminator */ +}; + +static const struct hda_fixup cs8409_apple_fixups[] = { + [CS8409_MBP131] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp131_pincfgs, + .chained = true, + .chain_id = CS8409_APPLE_GPIO_0, + }, + [CS8409_APPLE_GPIO_0] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs_8409_apple_fixup_gpio, + }, + [CS8409_MBP143] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp143_pincfgs, + .chained = true, + .chain_id = CS8409_APPLE_GPIO, + }, + [CS8409_APPLE_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs_8409_apple_fixup_gpio, + }, +}; +#endif + + +// as per Apple we need to fix the pin config for the linein nid (it defaults as no conn, Line Out which messes the auto config) +// (assuming this swaps as nid swaps for macbook pro/imac) +// except define device as line in not mic in as per Apple, remove the 0x00000100 which is apparently AC_DEFCFG_MISC_NO_PRESENCE +// plus make it a jack like the other jack inputs (so 0x00800001 not 0x90a00101 as per Apple) + +static const struct hda_pintbl macbook_pro_pincfgs[] = { + { 0x45, 0x00800101 }, + { } +}; +static const struct hda_pintbl imac_pincfgs[] = { + { 0x44, 0x00800101 }, + { } +}; + + +static void cs_8409_cs42l83_unsolicited_response(struct hda_codec *codec, unsigned int res); + +static void cs_8409_cs42l83_callback(struct hda_codec *codec, struct hda_jack_callback *event) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_cs42l83_callback %pF\n", cs_8409_cs42l83_callback); + + // for kernel version 4 we stored the unsol res data in private_data + // - now its a separate entity + ////cs_8409_cs42l83_unsol_event_handler(codec, event->private_data); + + cs_8409_cs42l83_unsol_event_handler(codec, event->unsol_res); + + mycodec_info(codec, "cs_8409_cs42l83_callback end\n"); +} + + +static void cs_8409_cs42l83_unsol_event_handler(struct hda_codec *codec, unsigned int unsol_res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler\n"); + + + // print the stored unsol res which seems to be the GPIO pins state + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler - event unsol_res 0x%08x\n",unsol_res); + + cs_8409_cs42l83_unsolicited_response(codec, unsol_res); + + + // now think timers not the way to go + // patch_realtek.c has to deal with similar issues of plugin, headset detection + // and just uses msleep calls + //mod_timer(&cs_8409_hp_timer, jiffies + msecs_to_jiffies(250)); + + // the delayed_work feature might be a way to go tho + + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler end\n"); +} + + +// we have 4 automute hooks +// void (*automute_hook)(struct hda_codec *codec); +// void (*hp_automute_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); +// void (*line_automute_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); +// void (*mic_autoswitch_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); + +static void cs_8409_automute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + dev_info(hda_codec_dev(codec), "cs_8409_automute called\n"); +} + +static int cs_8409_boot_setup(struct hda_codec *codec); + + +static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + +static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + +// new attempt if we decide to use the existing fixup mechanism to handle setup + +#ifdef APPLE_FIXUPS +static int patch_cs8409_apple_nouse(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + int err; + int itm; + + snd_hda_pick_fixup(codec, cs8409_apple_models, cs8409_apple_fixup_tbl, cs8409_apple_fixups); + + mycodec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, + codec->bus->pci->subsystem_vendor, + codec->bus->pci->subsystem_device); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = cs8409_apple_parse_auto_config(codec); + if (err < 0) { + cs8409_free(codec); + return err; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; +} +#endif + + +// for Apple we need multiple versions because so far macbook pro and imacs use different nids +static int cs8409_cs42l83_macbook_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ +#ifdef APPLE_PINSENSE_FIXUP + switch (nid) { + case CS8409_CS42L83_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_HP_MIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + case CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static int cs8409_cs42l83_imac_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ +#ifdef APPLE_PINSENSE_FIXUP + switch (nid) { + case CS8409_CS42L83_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_HP_MIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_IMAC_LINEIN_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static int cs8409_cs42l83_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + // we have confirmed this is being called + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + +#ifdef APPLE_PINSENSE_FIXUP + // or we could have a single function and the following + if (nid == CS8409_CS42L83_HP_PIN_NID) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->hp_jack_in); + //*res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } else if (nid == CS8409_CS42L83_HP_MIC_PIN_NID) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->mic_jack_in); + //*res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } else if (nid == spec->linein_nid) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->linein_jack_in); + //*res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static struct cs8409_apple_spec *cs8409_apple_alloc_spec(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + codec->spec = spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + spec->codec = codec; +#endif + //spec->vendor_nid = vendor_nid; + + // this allows power control per nid - for Apple ignoring all power control for now + // this removes eg power up/down on jack plugin/unplug + //codec->power_save_node = 1; + // looks like we need an explicit set - 1 maybe default + codec->power_save_node = 0; +#if 0 + mutex_init(&spec->i2c_mux); + INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); +#endif + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + + +static int patch_cs8409_apple(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec; + int err; + int itm; + int i; + //hda_nid_t *dac_nids_ptr = NULL; + const struct hda_pincfg *pin; + int fixup_found = 0; + + int explicit = 0; + + //struct hda_pcm *info = NULL; + //struct hda_pcm_stream *hinfo = NULL; + + myprintk("snd_hda_intel: Patching for CS8409 Apple - explicit %d\n", explicit); + //mycodec_info(codec, "Patching for CS8409 Apple - %d\n", explicit); + + //dump_stack(); + + spec = cs8409_apple_alloc_spec(codec); + if (!spec) + return -ENOMEM; + + + spec->vendor_nid = CS8409_VENDOR_NID; + + spec->beep_nid = CS8409_BEEP_NID; + + spec->use_data = 0; + + + // so it appears we need to explicitly apply pre probe fixups here + // note that if the pinconfigs lists are empty the pin config fixup + // is effectively ignored + + //myprintk("cs8409 - 1\n"); + //snd_hda_pick_fixup(codec, cs8409_apple_models, cs8409_apple_fixup_tbl, cs8409_apple_fixups); + + //mycodec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, + // codec->bus->pci->subsystem_vendor, + // codec->bus->pci->subsystem_device); + + //myprintk("cs8409 - 2\n"); + //snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + + // check if we have an implementation for the current machine + // probably should update the fixup tables and use those + // for the moment just replicate the ifs being used + // at the moment seem to get some crash if dont have a known subsystem id + fixup_found = 0; + if (codec->core.subsystem_id == 0x106b3900) { + // macbook pro 14,3 + fixup_found = 1; + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + // macbook pro 14,1?13,1, 14,2 + fixup_found = 1; + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // imac 18,3, 19,1, 18,2, 18,1 + fixup_found = 1; + } + + if (!fixup_found) { + dev_err(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + return -ENODEV; + } + + + + // the following coding is the HDA_FIXUP_ACT_PRE_PROBE phase + + + //issue init verbs here + //snd_hda_add_verbs(codec, cs8409_cs42l83_init_verbh); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + + // now going with the Dell way of handling PIN_SENSE for jack plug/unplug events so need this + + myprintk("snd_hda_intel: exec verb setup START\n"); + spec->exec_verb = codec->core.exec_verb; + codec->core.exec_verb = cs8409_cs42l83_exec_verb; +/* + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + codec->core.exec_verb = cs8409_cs42l83_imac_exec_verb; + } + else: + { + codec->core.exec_verb = cs8409_cs42l83_macbook_exec_verb; + } + */ + myprintk("snd_hda_intel: exec verb setup DONE\n"); + + spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l83_codec; + spec->num_scodecs = 1; + spec->scodecs[CS8409_CODEC0]->codec = codec; + + //codec->patch_ops = cs8409_cs42l83_patch_ops; + + if (explicit) + { + //codec->patch_ops = cs_8409_apple_patch_ops_explicit; + } + else + spec->vops = &cs_8409_apple_vops; + + + // not sure about these + // the suppress_vmaster is likely reasonable as the Apple way has no dynamic volume controls on either the 8409 chip + // or the amp/cs42l83 chips - the volume is set on boot to a fixed level and never changed + spec->gen.suppress_auto_mute = 1; + spec->gen.no_primary_hp = 1; + spec->gen.suppress_vmaster = 1; + + // we probably also want to suppress auto mic + //spec->gen.suppress_auto_mic = 1; + + // dell GPIO pins are : /* GPIO 5 out, 3,4 in */ + // what is true is the cs8409 interrupt GPIO is pin 1 + // aah - the reset GPIO pin is 0x02 + spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; + spec->gpio_data = 0; + //spec->gpio_mask = 0x03f; + if (codec->core.subsystem_id == 0x106b3900) { + spec->gpio_mask = 0x07; + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + spec->gpio_mask = 0x0f; + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + spec->gpio_mask = 0x0f; + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + // dell init : /* Basic initial sequence for specific hw configuration */ + //snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); + + + //snd_array_for_each(&codec->init_pins, i, pin) { + // hda_nid_t nid = pin->nid; + // u32 cfg = pin->cfg; + // myprintk("snd_hda_intel: print pincfg init 0x%02x: 0x%02x: 0x%08x\n", i, nid, cfg); + //} + //snd_array_for_each(&codec->driver_pins, i, pin) { + // hda_nid_t nid = pin->nid; + // u32 cfg = pin->cfg; + // myprintk("snd_hda_intel: print pincfg driver 0x%02x: 0x%02x: 0x%08x\n", i, nid, cfg); + //} + + //for (i = 0; i < codec->core.num_nodes; i++) + // myprintk("snd_hda_intel: print wcaps pre 0x%02x 0x%08x\n", i, codec->wcaps[i]); + + + // as per Apple we need to fix the pin config for the linein nid (it defaults as no conn, Line Out which messes the auto config) + // (assuming this swaps as nid swaps for macbook pro/imac) + // except define device as line in not mic in as per Apple plus make it a jack like + // the other jack inputs (so 0x00800101 not 0x90a00101 as per Apple) + + //static const struct hda_pintbl macbook_pro_pincfgs[] = { + // { 0x45, 0x00800101 }, + // { } + //}; + //static const struct hda_pintbl imac_pincfgs[] = { + // { 0x44, 0x00800101 }, + // { } + //}; + + + // cs8409_fix_caps overrides the jack nid to AC_PINCAP_IMP_SENSE and AC_PINCAP_PRES_DETECT parameters + // and adds AC_WCAP_UNSOL_CAP to the wcaps + // - not sure we should do this for AC_WCAP_UNSOL_CAP for Apple + // - but that depends on how we implement features + // currently we re-write snd_hda_jack_detect_enable_callback_mst and remove the AC_VERB_SET_UNSOLICITED_ENABLE + // setup (because we get unsol responses by default) + // but new idea is could fixup the exec_verb replacement and skip any AC_VERB_SET_UNSOLICITED_ENABLE + // commands received + +#if 1 + // we have one jack but 2 nids to setup - one for output and one for input + // the headphone output/input is same for both imac and macbook pros + //cs8409_fix_caps(codec, CS8409_CS42L83_HP_PIN_NID); + //cs8409_fix_caps(codec, CS8409_CS42L83_HP_MIC_PIN_NID); + // version of cs8409_fix_caps with some debug prints + cs8409_fix_caps_debug(codec, CS8409_CS42L83_HP_PIN_NID); + cs8409_fix_caps_debug(codec, CS8409_CS42L83_HP_MIC_PIN_NID); + + // we also need to setup the linein nid (why is there no lineout??) + // (note that the internal mike is permanent - on the other hand we do need to turn it off if headset with mike + // is plugged in???) + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs8409_fix_caps(codec, CS8409_CS42L83_IMAC_LINEIN_PIN_NID); + } else { + cs8409_fix_caps(codec, CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID); + } +#endif + + //for (i = 0; i < codec->core.num_nodes; i++) + // myprintk("snd_hda_intel: print wcaps post 0x%02x 0x%08x\n", i, codec->wcaps[i]); + + + //if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // snd_hda_apply_pincfgs(codec, imac_pincfgs); + //else + // snd_hda_apply_pincfgs(codec, macbook_pro_pincfgs); + + + // note that the auto config doesnt issue any PIN_SENSE commands but it does use the PINCAP data + // check_auto_mute_availability uses the PINCAP to determine if a nid can have a jack + // via is_jack_detectable - however because suppress_automute is set to 1 + // check_auto_mute_availability exits before calling this function + // maybe because check_auto_mic_availability is also called which also calls is_jack_detectable + // and is not suppressed + // so from this we can gather that all auto_mute/automute functionality must be handled explicitly + // (and not from eg snd_hda_gen_hp_automute/snd_hda_gen_line_automute) + // whereas auto_mic functionality is handled by the generic functions eg snd_hda_gen_mic_autoswitch + + + // Analysis of jack reporting so far: + // the default coding for generic jack reporting seems to be as follows: + // the default snd_hda_jack_unsol_event sets jack_dirty to 1 for the jack entry for the nid + // connected to the jack + // calls any callbacks for jack events (as set by snd_hda_jack_detect_enable_callback_mst) + // and then calls snd_hda_jack_report_sync + // snd_hda_jack_report_sync is the critically important function + // this function calls jack_detect_update which uses an AC_VERB_GET_PIN_SENSE verb to detect + // current jack status then unsets jack_dirty to 0 + // then for all jacks the current pin sense state AND button state is reported to the user level + // via snd_jack_report (a function of jack.c in core directory) + // which updates user level with a snd_kctl_jack_report - a function of ctljack.c in core + // which issues a snd_ctl_notify using SNDRV_CTL_EVENT_MASK_VALUE and the previously prepared + // pin sense state AND button state + + + + // tip sense setups done here + + +#else + if (explicit) + { + //codec->patch_ops = cs_8409_apple_patch_ops_explicit; + } + else + spec->vops = &cs_8409_apple_vops; +#endif + + // moved to post auto config + + //spec->gen.pcm_playback_hook = cs_8409_playback_pcm_hook; + + //spec->gen.pcm_capture_hook = cs_8409_capture_pcm_hook; + + //spec->gen.automute_hook = cs_8409_automute; + + //timer_setup(&cs_8409_hp_timer, cs_8409_hp_timer_callback, 0); + + + // use this to cause unsolicited responses to be stored + // but not run + spec->block_unsol = 0; + + INIT_LIST_HEAD(&spec->unsol_list); + + for (itm=0; itm<10; itm++) + { spec->unsol_items_prealloc_used[itm] = 0; } + + + // for the moment set initial jack status to not present + // we will detect if have jack plugged in on boot later + spec->jack_present = 0; + + + spec->headset_type = 0; + + spec->have_mike = 0; + + spec->have_buttons = 0; + + spec->playing = 0; + spec->capturing = 0; + + spec->headset_play_format_setup_needed = 1; + spec->headset_capture_format_setup_needed = 1; + + spec->headset_presetup_done = 0; + + + // use this to distinguish which unsolicited phase we are in + // for the moment - we only seem to get a tag of 0x37 and dont see any + // different tags being setup in OSX logs + spec->headset_phase = 0; + + spec->headset_enable = 0; + + + // setup the intmike and linein nids + // these are swapped between macbook pros and imacs + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->intmike_nid = 0x45; + spec->intmike_adc_nid = 0x23; + spec->linein_nid = 0x44; + spec->linein_amp_nid = 0x22; + } + else + { + spec->intmike_nid = 0x44; + spec->intmike_adc_nid = 0x22; + spec->linein_nid = 0x45; + spec->linein_amp_nid = 0x23; + } + + myprintk("snd_hda_intel: intmike_nid is 0x%02x\n", spec->intmike_nid); + myprintk("snd_hda_intel: intmike_acc_nid is 0x%02x\n", spec->intmike_adc_nid); + + + // ASP is Audio Serial Port + // DMIC is Digital Microphone Input + // + // so it appears that while the intmike/linein nid functions are swapped between macbook pro and imacs + // the digital mike paths associated with the nids are fixed - which means the digital mike path + // swaps between macbooks and imac + // ie nid 0x44 -> 0x22 path 0x82 : 0x0001 ie DMIC1 + // ie nid 0x45 -> 0x23 path 0x82 : 0x0002 ie DMIC2 + + // current status 0x82 reg: + // macbook pro and imac use 0x5400 for speaker amps - ASP1 (nid indpendent??) + // macbook pro and imac use 0xa800 for headset amp - ASP2 (nid indpendent??) + // macbook pro use 0x0001 for internal mike? - DMIC1 (nid dependent) + // macbook imac use 0x0002 for internal mike? - DMIC2 (nid dependent) + // macbook pro use 0x0002 for linein? - DMIC2 (nid dependent) + // macbook imac use 0x0001 for linein? - DMIC1 (nid dependent) + + // headphone/headset output path + // nid 0x0a -> 0x2c + + // headset mike path + // nid 0x3c -> 0x1a + + // combining data from cs4208_...inf with new 8409 updates in patch_cirrus for DEL laptops + // it seems reg 0x0009 is associated with the digital mikes + // vendor reg 0x0009 - 0x0023 : DMIC1_MO=10b, DMIC1/2_SR=1 + // - 0x0003 : DMIC1_MO=00b, DMIC1/2_SR=1 + // - 0x0043 : DMIC2_MO=01b, DMIC1/2_SR=1 + // - 0x0083 : DMIC2_MO=10b, DMIC1/2_SR=1 - this is my guess + // note that Apple also sets bit 0x0010 which so far is undocumented as is not consistent with above data + // - 0x0033 : DMIC1_MO=11b, DMIC1/2_SR=1 + // - 0x0093 : DMIC2_MO=10b, DMIC1_MO=01b?, DMIC1/2_SR=1 + + // it appears we need to swap a number of vendor node register fields because of the above path swap + // vendor reg 0x0009 - associated with internal mike from macbook pros + // OSX function setConnectionSelect + // macbook pro init 0x0013 -> 0x0033 ie 0x0020 bit set + // imac init 0x0013 -> 0x0093 ie 0x0080 bit set + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->reg9_intmike_dmic_mo = 0x0080; // DMIC2_MO=10b + } + else + { + spec->reg9_intmike_dmic_mo = 0x0020; // DMIC1_MO=10b + } + + // vendor reg 0x0082 - this is a very complex reg + // - it appears to contain 2 sort of separate items - the ASP1 and ASP2 enables and the DMIC1/DMIC2 SCL enables + // - 0xfc03 : ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1/2_SCL_EN=1 (was DMIC1_SCL_EN in comments but thinks thats wrong given below) + // - 0xfc01 : (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1) + // - 0xff03 : (ASP1/2_xxx_EN = 1, DMIC1/2_SCL_EN = 1) + // - 0xfd02 : (ASP1/2_xxx_EN = 1, ASP2_MCLK_EN = 0, DMIC2_SCL_EN = 1) + // - 0xfe03 : (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 0, DMIC1/2_SCL_EN = 1) + // - so (ASP1_MCLK_EN is 0x0100 and ASP2_MCLK_EN is 0x0200) + // - from the OSX codes we seem to have + // - 0xa800 : ASP2_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + // - 0x5400 : ASP1_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + + // vendor reg 0x0082 - we need to update the DMIC bit fields but not the ASP bit fields for macbook/imac switch + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->reg82_intmike_dmic_scl = 0x0002; // DMIC2_SCL_EN + spec->reg82_linein_dmic_scl = 0x0001; // DMIC1_SCL_EN + } + else + { + spec->reg82_intmike_dmic_scl = 0x0001; // DMIC1_SCL_EN + spec->reg82_linein_dmic_scl = 0x0002; // DMIC2_SCL_EN + } + + + // the jack detect enable callback can be either in PRE_PROBE or PROBE phase + + myprintk("snd_hda_intel: cs 8409 jack used %d\n",codec->jacktbl.used); + + // we need to figure out how to setup the jack detect callback + // not clear what nid should be used - 0x01 or 0x47 + // added a tag argument because we seem to get a tag + // so far the tag seems to be 0x37 + //cs_8409_hda_jack_detect_enable_callback(codec, 0x01, 0, 0x37, cs_8409_cs42l83_callback); + + //myprintk("snd_hda_intel: cs 8409 jack used callback %d\n",codec->jacktbl.used); + + + // cs8409_pinmux_init(codec); + + + // so it appears we dont get interrupts in the auto config stage + + if (!explicit) + { + + myprintk("snd_hda_intel: pre cs_8409_apple_parse_auto_config\n"); + + err = cs_8409_apple_parse_auto_config(codec); + if (err < 0) + goto error; + + myprintk("snd_hda_intel: post cs_8409_apple_parse_auto_config\n"); + + cs_8409_dump_auto_config(codec, "post cs_8409_apple_parse_auto_config"); + } + + + // dump the rates/format of the afg node + // still havent figured out how the user space gets the allowed formats + // ah - may have figured this + // except that at this point this is NULL - we need to be after build pcms + //info = spec->gen.pcm_rec[0]; + //if (info != NULL) + //{ + // hinfo = &(info->stream[SNDRV_PCM_STREAM_PLAYBACK]); + // if (hinfo != NULL) + // mycodec_dbg(codec, "playback info stream nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + // else + // mycodec_dbg(codec, "playback info stream NULL\n"); + //} + //else + // mycodec_dbg(codec, "playback info NULL\n"); + + + // the following code would be the HDA_FIXUP_ACT_PROBE phase + + + // new idea to try and use the gated/gating jack function as we have only one jack + // and this seems to be what gating/gated jacks seem to be designed for + + + + // this is how to fix the format for the streams + // dell : /* Fix Sample Rate to 48kHz */ + //spec->gen.stream_analog_playback = &cs42l83_apple_pcm_analog_playback; + //spec->gen.stream_analog_capture = &cs42l83_apple_pcm_analog_capture; + + + spec->gen.pcm_playback_hook = cs_8409_playback_pcm_hook; + + spec->gen.pcm_capture_hook = cs_8409_capture_pcm_hook; + + spec->gen.automute_hook = cs_8409_automute; + + + // so this seems to be how we setup volume controls for the headphones + // - but this sets up controls on the 8409 to directly update the cs42l42 + // - this is not how apple does things - all volume controls must occur + // prior to this driver + // the problem is this means we need some pulse/alsa configuration to setup + // such controls + + // del : /* Set initial DMIC volume to -26 dB */ + //snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, + // HDA_INPUT, 0, 0xff, 0x19); + //snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", + // &cs42l42_dac_volume_mixer); + //snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", + // &cs42l42_adc_volume_mixer); + + + // del : /* Disable Unsolicited Response during boot */ + //cs8409_enable_ur(codec, 0); + + + snd_hda_codec_set_name(codec, "CS8409/CS42L83"); + + + myprintk("snd_hda_intel: cs 8409 jack used post %d\n",codec->jacktbl.used); + +#ifdef MYSOUNDDEBUGFULL + cs_8409_dump_callback(codec); +#else +#ifdef MYSOUNDDEBUG + cs_8409_dump_callback(codec); +#endif +#endif + + + + err = cs_8409_boot_setup(codec); + if (err < 0) + goto error; + + // update the headset phase + spec->headset_phase = 2; + + spec->play_init = 0; + spec->capture_init = 0; + + // init the last play time +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + ktime_get_real_ts64(&(spec->last_play_time)); +#else + getnstimeofday(&(spec->last_play_time)); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + ktime_get_real_ts64(&(spec->first_play_time)); +#else + getnstimeofday(&(spec->first_play_time)); +#endif + + myprintk("snd_hda_intel: Post Patching for CS8409 Apple\n"); + //mycodec_info(codec, "Post Patching for CS8409 Apple\n"); + + return 0; + + error: + cs_8409_apple_free(codec); + return err; +} + + +// for the moment split the new code into an include file + +#include "patch_cirrus_new84.h" + + +// new function to use "vendor" defined commands to run +// a specific code +// has to be here to use functions defined in patch_cirrus_new84.h + +static unsigned int +cs_8409_extended_codec_verb(struct hda_codec *codec, hda_nid_t nid, + int flags, + unsigned int verb, unsigned int parm) +{ + //static inline unsigned int cs_8409_vendor_i2cRead(struct hda_codec *codec, unsigned int i2c_address, + // unsigned int i2c_reg, unsigned int paged) + unsigned int retval1 = 0; + unsigned int retval2 = 0; + unsigned int retval3 = 0; + unsigned int retval4 = 0; + unsigned int retval = 0; + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb nid 0x%02x flags 0x%x verb 0x%03x parm 0x%04x\n", nid, flags, verb, parm); + + if ((verb & 0x0ff8) == 0xf78) + { + retval1 = cs_8409_vendor_i2cWrite(codec, 0x64, 0x2d, parm, 0); + retval2 = cs_8409_vendor_i2cWrite(codec, 0x62, 0x2d, parm, 0); + retval3 = cs_8409_vendor_i2cWrite(codec, 0x74, 0x2d, parm, 0); + retval4 = cs_8409_vendor_i2cWrite(codec, 0x72, 0x2d, parm, 0); + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 1 0x%x\n",retval1); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 2 0x%x\n",retval2); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 3 0x%x\n",retval3); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 4 0x%x\n",retval4); + } + else if ((verb & 0x0ff8) == 0xff8) + { + retval1 = cs_8409_vendor_i2cRead(codec, 0x64, 0x2d, 0); + retval2 = cs_8409_vendor_i2cRead(codec, 0x62, 0x2d, 0); + retval3 = cs_8409_vendor_i2cRead(codec, 0x74, 0x2d, 0); + retval4 = cs_8409_vendor_i2cRead(codec, 0x72, 0x2d, 0); + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 1 0x%x\n",retval1); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 2 0x%x\n",retval2); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 3 0x%x\n",retval3); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 4 0x%x\n",retval4); + } + + + retval = retval1; + + return retval; +} + +#ifdef ADD_EXTENDED_VERB +static void cs_8409_set_extended_codec_verb(void) +{ + snd_hda_set_extended_codec_verb(cs_8409_extended_codec_verb); +} +#endif + + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h new file mode 100644 index 0000000..4bb1a36 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h @@ -0,0 +1,2452 @@ + +static void setup_reset_and_clear(struct hda_codec *codec) +{ + + // so now really dont know why I skipped all this - maybe because they + // almost entirely reads?? + // - except it does clear all the pin configs + // plus does a DBL init + + int retval; + + + mycodec_info(codec, "command nid start setup_node_reset_and_clear\n"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 2); // 0x000f0002 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 3); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 4); // 0x000f0002 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00010001, 5); // 0x000f0004 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00020046, 6); // 0x001f0004 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000101, 7); // 0x001f0005 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_SUBSYSTEM_ID, 0x00000000, 0x106b3900, 8); // 0x001f2000 + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 9); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 10); // 0x000f0002 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000f, 0xe0000019, 11); // 0x001f000f + + + // AppleHDACodecGeneric::start(IOService*) + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_DBL_CODEC_RESET, 0x00000000); // 0x001fff00 + snd_hda_double_reset(codec); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 13); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 14); // 0x000f0002 + + + // AppleHDANode::initForNodeID(unsigned short, OSObject*, OSObject*) + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000101, 15); // 0x001f0005 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000f, 0xe0000019, 16); // 0x001f000f + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001a017f, 17); // 0x001f000a +// snd_hda: pcm params 1 bits: 16bit 24bit 32bit rates: 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 96kHz + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 18); // 0x001f000b +// snd_hda: stream format params 1 pcm + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 19); // 0x001f0012 +// snd_hda: amp capabilities 1 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 20); // 0x001f000d +// snd_hda: amp capabilities 1 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + + + // AppleHDAFunctionGroup::initForNodeID?? + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_SUBSYSTEM_ID, 0x00000000, 0x106b3900, 22); // 0x001f2000 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000008, 0x00010000, 23); // 0x001f0008 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0x00000000, 0x00000000, 24); // 0x001f1700 +// snd_hda: gpio direction 1 0x00 in in in in in in in in + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000011, 0xc0000008, 25); // 0x001f0011 +// snd_hda: gpio params 1 [('GPIO', 8), ('GPIO_WAKE', 1), ('GPO', 0), ('GPI', 0), ('GPIO_UNSOL', 1)] + + mycodec_info(codec, "command nid start setup_node_reset_and_clear end\n"); + +} + +static void init_read_all_nodes(struct hda_codec *codec) +{ + int retval; + + + // get node count + // note that there are 4 so called VirtualWidgets which are indexed after the 0x46 count from + // the vendor node at 0x47 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00020046, 26); // 0x001f0004 + + + // so apple reads parameters from all nodes + // but setting pin nodes SET_PIN_WIDGET_CONTROL to 0 + + // this appears to be a loop of calls to AppleHDAWidgetCS8409::initForNodeID for each node + // this calls AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) for each node + // and specifically deals with the BEEP node + + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 27); // 0x002f0005 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 28); // 0x002f0009 +// snd_hda: 2 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 29); // 0x002f000f + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 30); // 0x002f000a +// snd_hda: pcm params 2 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 31); // 0x002f000b +// snd_hda: stream format params 2 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 32); // 0x002f0012 +// snd_hda: amp capabilities 2 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 33); // 0x002f000d +// snd_hda: amp capabilities 2 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 34); // 0x002f0009 +// snd_hda: 2 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 36); // 0x003f0005 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 37); // 0x003f0009 +// snd_hda: 3 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 38); // 0x003f000f + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 39); // 0x003f000a +// snd_hda: pcm params 3 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 40); // 0x003f000b +// snd_hda: stream format params 3 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 41); // 0x003f0012 +// snd_hda: amp capabilities 3 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 42); // 0x003f000d +// snd_hda: amp capabilities 3 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 43); // 0x003f0009 +// snd_hda: 3 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 45); // 0x004f0005 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 46); // 0x004f0009 +// snd_hda: 4 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 47); // 0x004f000f + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 48); // 0x004f000a +// snd_hda: pcm params 4 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 49); // 0x004f000b +// snd_hda: stream format params 4 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 50); // 0x004f0012 +// snd_hda: amp capabilities 4 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 51); // 0x004f000d +// snd_hda: amp capabilities 4 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 52); // 0x004f0009 +// snd_hda: 4 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 54); // 0x005f0005 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 55); // 0x005f0009 +// snd_hda: 5 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 56); // 0x005f000f + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 57); // 0x005f000a +// snd_hda: pcm params 5 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 58); // 0x005f000b +// snd_hda: stream format params 5 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 59); // 0x005f0012 +// snd_hda: amp capabilities 5 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 60); // 0x005f000d +// snd_hda: amp capabilities 5 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 61); // 0x005f0009 +// snd_hda: 5 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 63); // 0x006f0005 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 64); // 0x006f0009 +// snd_hda: 6 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 65); // 0x006f000f + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 66); // 0x006f000a +// snd_hda: pcm params 6 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 67); // 0x006f000b +// snd_hda: stream format params 6 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 68); // 0x006f0012 +// snd_hda: amp capabilities 6 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 69); // 0x006f000d +// snd_hda: amp capabilities 6 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 70); // 0x006f0009 +// snd_hda: 6 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 72); // 0x007f0005 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 73); // 0x007f0009 +// snd_hda: 7 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 74); // 0x007f000f + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 75); // 0x007f000a +// snd_hda: pcm params 7 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 76); // 0x007f000b +// snd_hda: stream format params 7 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 77); // 0x007f0012 +// snd_hda: amp capabilities 7 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 78); // 0x007f000d +// snd_hda: amp capabilities 7 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 79); // 0x007f0009 +// snd_hda: 7 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 81); // 0x008f0005 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 82); // 0x008f0009 +// snd_hda: 8 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 83); // 0x008f000f + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 84); // 0x008f000a +// snd_hda: pcm params 8 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 85); // 0x008f000b +// snd_hda: stream format params 8 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 86); // 0x008f0012 +// snd_hda: amp capabilities 8 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 87); // 0x008f000d +// snd_hda: amp capabilities 8 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 88); // 0x008f0009 +// snd_hda: 8 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 90); // 0x009f0005 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 91); // 0x009f0009 +// snd_hda: 9 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 92); // 0x009f000f + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 93); // 0x009f000a +// snd_hda: pcm params 9 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 94); // 0x009f000b +// snd_hda: stream format params 9 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 95); // 0x009f0012 +// snd_hda: amp capabilities 9 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 96); // 0x009f000d +// snd_hda: amp capabilities 9 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 97); // 0x009f0009 +// snd_hda: 9 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 99); // 0x00af0005 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 100); // 0x00af0009 +// snd_hda: 10 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 101); // 0x00af000f + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 102); // 0x00af000a +// snd_hda: pcm params 10 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 103); // 0x00af000b +// snd_hda: stream format params 10 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 104); // 0x00af0012 +// snd_hda: amp capabilities 10 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 105); // 0x00af000d +// snd_hda: amp capabilities 10 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 106); // 0x00af0009 +// snd_hda: 10 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 108); // 0x00bf0005 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 109); // 0x00bf0009 +// snd_hda: 11 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 110); // 0x00bf000f + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 111); // 0x00bf000a +// snd_hda: pcm params 11 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 112); // 0x00bf000b +// snd_hda: stream format params 11 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 113); // 0x00bf0012 +// snd_hda: amp capabilities 11 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 114); // 0x00bf000d +// snd_hda: amp capabilities 11 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 115); // 0x00bf0009 +// snd_hda: 11 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 117); // 0x00cf0005 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 118); // 0x00cf0009 +// snd_hda: 12 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 119); // 0x00cf000f + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 120); // 0x00cf000a +// snd_hda: pcm params 12 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 121); // 0x00cf000b +// snd_hda: stream format params 12 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 122); // 0x00cf0012 +// snd_hda: amp capabilities 12 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 123); // 0x00cf000d +// snd_hda: amp capabilities 12 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 124); // 0x00cf0009 +// snd_hda: 12 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 126); // 0x00df0005 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 127); // 0x00df0009 +// snd_hda: 13 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 128); // 0x00df000f + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 129); // 0x00df000a +// snd_hda: pcm params 13 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 130); // 0x00df000b +// snd_hda: stream format params 13 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 131); // 0x00df0012 +// snd_hda: amp capabilities 13 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 132); // 0x00df000d +// snd_hda: amp capabilities 13 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 133); // 0x00df0009 +// snd_hda: 13 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 135); // 0x00ef0005 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 136); // 0x00ef0009 +// snd_hda: 14 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 137); // 0x00ef000f + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 138); // 0x00ef000a +// snd_hda: pcm params 14 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 139); // 0x00ef000b +// snd_hda: stream format params 14 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 140); // 0x00ef0012 +// snd_hda: amp capabilities 14 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 141); // 0x00ef000d +// snd_hda: amp capabilities 14 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 142); // 0x00ef0009 +// snd_hda: 14 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 144); // 0x00ff0005 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 145); // 0x00ff0009 +// snd_hda: 15 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 146); // 0x00ff000f + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 147); // 0x00ff000a +// snd_hda: pcm params 15 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 148); // 0x00ff000b +// snd_hda: stream format params 15 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 149); // 0x00ff0012 +// snd_hda: amp capabilities 15 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 150); // 0x00ff000d +// snd_hda: amp capabilities 15 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 151); // 0x00ff0009 +// snd_hda: 15 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 153); // 0x010f0005 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 154); // 0x010f0009 +// snd_hda: 16 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 155); // 0x010f000f + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 156); // 0x010f000a +// snd_hda: pcm params 16 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 157); // 0x010f000b +// snd_hda: stream format params 16 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 158); // 0x010f0012 +// snd_hda: amp capabilities 16 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 159); // 0x010f000d +// snd_hda: amp capabilities 16 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 160); // 0x010f0009 +// snd_hda: 16 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 162); // 0x011f0005 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 163); // 0x011f0009 +// snd_hda: 17 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 164); // 0x011f000f + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 165); // 0x011f000a +// snd_hda: pcm params 17 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 166); // 0x011f000b +// snd_hda: stream format params 17 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 167); // 0x011f0012 +// snd_hda: amp capabilities 17 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 168); // 0x011f000d +// snd_hda: amp capabilities 17 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 169); // 0x011f0009 +// snd_hda: 17 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 171); // 0x012f0005 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 172); // 0x012f0009 +// snd_hda: 18 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 173); // 0x012f000f + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 174); // 0x012f000a +// snd_hda: pcm params 18 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 175); // 0x012f000b +// snd_hda: stream format params 18 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 176); // 0x012f0012 +// snd_hda: amp capabilities 18 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 177); // 0x012f000d +// snd_hda: amp capabilities 18 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 178); // 0x012f0009 +// snd_hda: 18 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 179); // 0x012f000e + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000034, 180); // 0x012f0200 +// snd_hda: connection list 18 <- 52 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 182); // 0x013f0005 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 183); // 0x013f0009 +// snd_hda: 19 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 184); // 0x013f000f + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 185); // 0x013f000a +// snd_hda: pcm params 19 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 186); // 0x013f000b +// snd_hda: stream format params 19 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 187); // 0x013f0012 +// snd_hda: amp capabilities 19 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 188); // 0x013f000d +// snd_hda: amp capabilities 19 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 189); // 0x013f0009 +// snd_hda: 19 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 190); // 0x013f000e + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000035, 191); // 0x013f0200 +// snd_hda: connection list 19 <- 53 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 193); // 0x014f0005 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 194); // 0x014f0009 +// snd_hda: 20 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 195); // 0x014f000f + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 196); // 0x014f000a +// snd_hda: pcm params 20 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 197); // 0x014f000b +// snd_hda: stream format params 20 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 198); // 0x014f0012 +// snd_hda: amp capabilities 20 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 199); // 0x014f000d +// snd_hda: amp capabilities 20 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 200); // 0x014f0009 +// snd_hda: 20 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 201); // 0x014f000e + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000036, 202); // 0x014f0200 +// snd_hda: connection list 20 <- 54 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 204); // 0x015f0005 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 205); // 0x015f0009 +// snd_hda: 21 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 206); // 0x015f000f + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 207); // 0x015f000a +// snd_hda: pcm params 21 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 208); // 0x015f000b +// snd_hda: stream format params 21 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 209); // 0x015f0012 +// snd_hda: amp capabilities 21 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 210); // 0x015f000d +// snd_hda: amp capabilities 21 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 211); // 0x015f0009 +// snd_hda: 21 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 212); // 0x015f000e + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000037, 213); // 0x015f0200 +// snd_hda: connection list 21 <- 55 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 215); // 0x016f0005 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 216); // 0x016f0009 +// snd_hda: 22 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 217); // 0x016f000f + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 218); // 0x016f000a +// snd_hda: pcm params 22 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 219); // 0x016f000b +// snd_hda: stream format params 22 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 220); // 0x016f0012 +// snd_hda: amp capabilities 22 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 221); // 0x016f000d +// snd_hda: amp capabilities 22 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 222); // 0x016f0009 +// snd_hda: 22 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 223); // 0x016f000e + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000038, 224); // 0x016f0200 +// snd_hda: connection list 22 <- 56 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 226); // 0x017f0005 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 227); // 0x017f0009 +// snd_hda: 23 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 228); // 0x017f000f + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 229); // 0x017f000a +// snd_hda: pcm params 23 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 230); // 0x017f000b +// snd_hda: stream format params 23 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 231); // 0x017f0012 +// snd_hda: amp capabilities 23 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 232); // 0x017f000d +// snd_hda: amp capabilities 23 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 233); // 0x017f0009 +// snd_hda: 23 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 234); // 0x017f000e + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000039, 235); // 0x017f0200 +// snd_hda: connection list 23 <- 57 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 237); // 0x018f0005 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 238); // 0x018f0009 +// snd_hda: 24 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 239); // 0x018f000f + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 240); // 0x018f000a +// snd_hda: pcm params 24 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 241); // 0x018f000b +// snd_hda: stream format params 24 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 242); // 0x018f0012 +// snd_hda: amp capabilities 24 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 243); // 0x018f000d +// snd_hda: amp capabilities 24 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 244); // 0x018f0009 +// snd_hda: 24 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 245); // 0x018f000e + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003a, 246); // 0x018f0200 +// snd_hda: connection list 24 <- 58 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 248); // 0x019f0005 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 249); // 0x019f0009 +// snd_hda: 25 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 250); // 0x019f000f + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 251); // 0x019f000a +// snd_hda: pcm params 25 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 252); // 0x019f000b +// snd_hda: stream format params 25 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 253); // 0x019f0012 +// snd_hda: amp capabilities 25 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 254); // 0x019f000d +// snd_hda: amp capabilities 25 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 255); // 0x019f0009 +// snd_hda: 25 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 256); // 0x019f000e + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003b, 257); // 0x019f0200 +// snd_hda: connection list 25 <- 59 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 259); // 0x01af0005 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 260); // 0x01af0009 +// snd_hda: 26 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 261); // 0x01af000f + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 262); // 0x01af000a +// snd_hda: pcm params 26 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 263); // 0x01af000b +// snd_hda: stream format params 26 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 264); // 0x01af0012 +// snd_hda: amp capabilities 26 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 265); // 0x01af000d +// snd_hda: amp capabilities 26 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 266); // 0x01af0009 +// snd_hda: 26 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 267); // 0x01af000e + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003c, 268); // 0x01af0200 +// snd_hda: connection list 26 <- 60 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 270); // 0x01bf0005 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 271); // 0x01bf0009 +// snd_hda: 27 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 272); // 0x01bf000f + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 273); // 0x01bf000a +// snd_hda: pcm params 27 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 274); // 0x01bf000b +// snd_hda: stream format params 27 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 275); // 0x01bf0012 +// snd_hda: amp capabilities 27 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 276); // 0x01bf000d +// snd_hda: amp capabilities 27 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 277); // 0x01bf0009 +// snd_hda: 27 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 278); // 0x01bf000e + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003d, 279); // 0x01bf0200 +// snd_hda: connection list 27 <- 61 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 281); // 0x01cf0005 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 282); // 0x01cf0009 +// snd_hda: 28 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 283); // 0x01cf000f + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 284); // 0x01cf000a +// snd_hda: pcm params 28 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 285); // 0x01cf000b +// snd_hda: stream format params 28 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 286); // 0x01cf0012 +// snd_hda: amp capabilities 28 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 287); // 0x01cf000d +// snd_hda: amp capabilities 28 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 288); // 0x01cf0009 +// snd_hda: 28 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 289); // 0x01cf000e + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003e, 290); // 0x01cf0200 +// snd_hda: connection list 28 <- 62 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 292); // 0x01df0005 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 293); // 0x01df0009 +// snd_hda: 29 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 294); // 0x01df000f + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 295); // 0x01df000a +// snd_hda: pcm params 29 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 296); // 0x01df000b +// snd_hda: stream format params 29 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 297); // 0x01df0012 +// snd_hda: amp capabilities 29 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 298); // 0x01df000d +// snd_hda: amp capabilities 29 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 299); // 0x01df0009 +// snd_hda: 29 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 300); // 0x01df000e + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003f, 301); // 0x01df0200 +// snd_hda: connection list 29 <- 63 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 303); // 0x01ef0005 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 304); // 0x01ef0009 +// snd_hda: 30 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 305); // 0x01ef000f + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 306); // 0x01ef000a +// snd_hda: pcm params 30 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 307); // 0x01ef000b +// snd_hda: stream format params 30 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 308); // 0x01ef0012 +// snd_hda: amp capabilities 30 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 309); // 0x01ef000d +// snd_hda: amp capabilities 30 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 310); // 0x01ef0009 +// snd_hda: 30 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 311); // 0x01ef000e + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000040, 312); // 0x01ef0200 +// snd_hda: connection list 30 <- 64 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 314); // 0x01ff0005 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 315); // 0x01ff0009 +// snd_hda: 31 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 316); // 0x01ff000f + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 317); // 0x01ff000a +// snd_hda: pcm params 31 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 318); // 0x01ff000b +// snd_hda: stream format params 31 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 319); // 0x01ff0012 +// snd_hda: amp capabilities 31 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 320); // 0x01ff000d +// snd_hda: amp capabilities 31 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 321); // 0x01ff0009 +// snd_hda: 31 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 322); // 0x01ff000e + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000041, 323); // 0x01ff0200 +// snd_hda: connection list 31 <- 65 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 325); // 0x020f0005 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 326); // 0x020f0009 +// snd_hda: 32 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 327); // 0x020f000f + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 328); // 0x020f000a +// snd_hda: pcm params 32 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 329); // 0x020f000b +// snd_hda: stream format params 32 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 330); // 0x020f0012 +// snd_hda: amp capabilities 32 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 331); // 0x020f000d +// snd_hda: amp capabilities 32 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 332); // 0x020f0009 +// snd_hda: 32 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 333); // 0x020f000e + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000042, 334); // 0x020f0200 +// snd_hda: connection list 32 <- 66 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 336); // 0x021f0005 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 337); // 0x021f0009 +// snd_hda: 33 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 338); // 0x021f000f + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 339); // 0x021f000a +// snd_hda: pcm params 33 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 340); // 0x021f000b +// snd_hda: stream format params 33 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 341); // 0x021f0012 +// snd_hda: amp capabilities 33 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 342); // 0x021f000d +// snd_hda: amp capabilities 33 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 343); // 0x021f0009 +// snd_hda: 33 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 344); // 0x021f000e + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000043, 345); // 0x021f0200 +// snd_hda: connection list 33 <- 67 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 347); // 0x022f0005 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 348); // 0x022f0009 +// snd_hda: 34 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 349); // 0x022f000f + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 350); // 0x022f000a +// snd_hda: pcm params 34 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 351); // 0x022f000b +// snd_hda: stream format params 34 pcm + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 352); // 0x022f0012 +// snd_hda: amp capabilities 34 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 353); // 0x022f000d +// snd_hda: amp capabilities 34 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 354); // 0x022f0009 +// snd_hda: 34 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + hda_set_node_power_state(codec, 0x22, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 356); // 0x022f000a +// snd_hda: pcm params 34 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 357); // 0x022f000b +// snd_hda: stream format params 34 pcm + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 358); // 0x022f000d +// snd_hda: amp capabilities 34 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 359); // 0x022f000e + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000044, 360); // 0x022f0200 +// snd_hda: connection list 34 <- 68 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 361); // 0x022f000f + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + hda_set_node_power_state(codec, 0x22, AC_PWRST_D3); + + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 364); // 0x023f0005 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 365); // 0x023f0009 +// snd_hda: 35 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 366); // 0x023f000f + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 367); // 0x023f000a +// snd_hda: pcm params 35 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 368); // 0x023f000b +// snd_hda: stream format params 35 pcm + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 369); // 0x023f0012 +// snd_hda: amp capabilities 35 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 370); // 0x023f000d +// snd_hda: amp capabilities 35 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 371); // 0x023f0009 +// snd_hda: 35 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + hda_set_node_power_state(codec, 0x23, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 373); // 0x023f000a +// snd_hda: pcm params 35 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 374); // 0x023f000b +// snd_hda: stream format params 35 pcm + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 375); // 0x023f000d +// snd_hda: amp capabilities 35 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 376); // 0x023f000e + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000045, 377); // 0x023f0200 +// snd_hda: connection list 35 <- 69 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 378); // 0x023f000f + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + hda_set_node_power_state(codec, 0x23, AC_PWRST_D3); + + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 381); // 0x024f0005 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 382); // 0x024f0009 +// snd_hda: 36 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 383); // 0x024f000f + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 384); // 0x024f0012 +// snd_hda: amp capabilities 36 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 385); // 0x024f000d +// snd_hda: amp capabilities 36 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 386); // 0x024f0009 +// snd_hda: 36 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x90100110, 387); // 0x024f1c00 +// snd_hda: pin config default 0x90100110 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 1 conn type 0 color 0 misc 1 def assoc 1 seq 0 +// snd_hda: pin config default 0x90100110 port conn Fixed loc ext Int loc geom N/A default device Speaker conn type Unknown color Unknown misc Jack Detect Override def assoc 1 seq 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 388); // 0x024f000c +// snd_hda: 36 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 389); // 0x024f000e + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000002, 390); // 0x024f0200 +// snd_hda: connection list 36 <- 2 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 391); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02470700 +// snd_hda: 36 [] + + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 394); // 0x025f0005 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 395); // 0x025f0009 +// snd_hda: 37 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 396); // 0x025f000f + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 397); // 0x025f0012 +// snd_hda: amp capabilities 37 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 398); // 0x025f000d +// snd_hda: amp capabilities 37 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 399); // 0x025f0009 +// snd_hda: 37 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x90100111, 400); // 0x025f1c00 +// snd_hda: pin config default 0x90100111 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 1 conn type 0 color 0 misc 1 def assoc 1 seq 1 +// snd_hda: pin config default 0x90100111 port conn Fixed loc ext Int loc geom N/A default device Speaker conn type Unknown color Unknown misc Jack Detect Override def assoc 1 seq 1 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 401); // 0x025f000c +// snd_hda: 37 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 402); // 0x025f000e + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000003, 403); // 0x025f0200 +// snd_hda: connection list 37 <- 3 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 404); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02570700 +// snd_hda: 37 [] + + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 407); // 0x026f0005 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 408); // 0x026f0009 +// snd_hda: 38 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 409); // 0x026f000f + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 410); // 0x026f0012 +// snd_hda: amp capabilities 38 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 411); // 0x026f000d +// snd_hda: amp capabilities 38 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 412); // 0x026f0009 +// snd_hda: 38 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 413); // 0x026f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 414); // 0x026f000c +// snd_hda: 38 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 415); // 0x026f000e + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000004, 416); // 0x026f0200 +// snd_hda: connection list 38 <- 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 417); // 0x026f0700 + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02670700 +// snd_hda: 38 [] + + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 420); // 0x027f0005 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 421); // 0x027f0009 +// snd_hda: 39 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 422); // 0x027f000f + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 423); // 0x027f0012 +// snd_hda: amp capabilities 39 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 424); // 0x027f000d +// snd_hda: amp capabilities 39 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 425); // 0x027f0009 +// snd_hda: 39 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 426); // 0x027f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 427); // 0x027f000c +// snd_hda: 39 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 428); // 0x027f000e + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000005, 429); // 0x027f0200 +// snd_hda: connection list 39 <- 5 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 430); // 0x027f0700 + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02770700 +// snd_hda: 39 [] + + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 433); // 0x028f0005 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 434); // 0x028f0009 +// snd_hda: 40 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 435); // 0x028f000f + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 436); // 0x028f0012 +// snd_hda: amp capabilities 40 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 437); // 0x028f000d +// snd_hda: amp capabilities 40 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 438); // 0x028f0009 +// snd_hda: 40 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 439); // 0x028f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 440); // 0x028f000c +// snd_hda: 40 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 441); // 0x028f000e + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000006, 442); // 0x028f0200 +// snd_hda: connection list 40 <- 6 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 443); // 0x028f0700 + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02870700 +// snd_hda: 40 [] + + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 446); // 0x029f0005 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 447); // 0x029f0009 +// snd_hda: 41 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 448); // 0x029f000f + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 449); // 0x029f0012 +// snd_hda: amp capabilities 41 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 450); // 0x029f000d +// snd_hda: amp capabilities 41 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 451); // 0x029f0009 +// snd_hda: 41 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 452); // 0x029f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 453); // 0x029f000c +// snd_hda: 41 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 454); // 0x029f000e + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000007, 455); // 0x029f0200 +// snd_hda: connection list 41 <- 7 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 456); // 0x029f0700 + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02970700 +// snd_hda: 41 [] + + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 459); // 0x02af0005 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 460); // 0x02af0009 +// snd_hda: 42 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 461); // 0x02af000f + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 462); // 0x02af0012 +// snd_hda: amp capabilities 42 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 463); // 0x02af000d +// snd_hda: amp capabilities 42 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 464); // 0x02af0009 +// snd_hda: 42 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 465); // 0x02af1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 466); // 0x02af000c +// snd_hda: 42 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 467); // 0x02af000e + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000008, 468); // 0x02af0200 +// snd_hda: connection list 42 <- 8 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 469); // 0x02af0700 + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02a70700 +// snd_hda: 42 [] + + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 472); // 0x02bf0005 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 473); // 0x02bf0009 +// snd_hda: 43 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 474); // 0x02bf000f + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 475); // 0x02bf0012 +// snd_hda: amp capabilities 43 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 476); // 0x02bf000d +// snd_hda: amp capabilities 43 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 477); // 0x02bf0009 +// snd_hda: 43 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 478); // 0x02bf1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 479); // 0x02bf000c +// snd_hda: 43 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 480); // 0x02bf000e + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000009, 481); // 0x02bf0200 +// snd_hda: connection list 43 <- 9 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 482); // 0x02bf0700 + snd_hda_codec_write(codec, 0x2b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02b70700 +// snd_hda: 43 [] + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 485); // 0x02cf0005 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 486); // 0x02cf0009 +// snd_hda: 44 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 487); // 0x02cf000f + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 488); // 0x02cf0012 +// snd_hda: amp capabilities 44 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 489); // 0x02cf000d +// snd_hda: amp capabilities 44 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 490); // 0x02cf0009 +// snd_hda: 44 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x002b4020, 491); // 0x02cf1c00 +// snd_hda: pin config default 0x002b4020 port conn 0 location 0x0 loc ext 0 loc geom 0 default device 2 conn type 11 color 4 misc 0 def assoc 2 seq 0 +// snd_hda: pin config default 0x002b4020 port conn Jack loc ext Ext loc geom N/A default device HP Out conn type Combination color Green misc undef def assoc 2 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 492); // 0x02cf000c +// snd_hda: 44 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 493); // 0x02cf000e + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000a, 494); // 0x02cf0200 +// snd_hda: connection list 44 <- 10 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 495); // 0x02cf0700 + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02c70700 +// snd_hda: 44 [] + + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 498); // 0x02df0005 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 499); // 0x02df0009 +// snd_hda: 45 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 500); // 0x02df000f + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 501); // 0x02df0012 +// snd_hda: amp capabilities 45 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 502); // 0x02df000d +// snd_hda: amp capabilities 45 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 503); // 0x02df0009 +// snd_hda: 45 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 504); // 0x02df1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 505); // 0x02df000c +// snd_hda: 45 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 506); // 0x02df000e + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000b, 507); // 0x02df0200 +// snd_hda: connection list 45 <- 11 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 508); // 0x02df0700 + snd_hda_codec_write(codec, 0x2d, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02d70700 +// snd_hda: 45 [] + + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 511); // 0x02ef0005 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 512); // 0x02ef0009 +// snd_hda: 46 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 513); // 0x02ef000f + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 514); // 0x02ef0012 +// snd_hda: amp capabilities 46 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 515); // 0x02ef000d +// snd_hda: amp capabilities 46 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 516); // 0x02ef0009 +// snd_hda: 46 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 517); // 0x02ef1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 518); // 0x02ef000c +// snd_hda: 46 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 519); // 0x02ef000e + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000c, 520); // 0x02ef0200 +// snd_hda: connection list 46 <- 12 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 521); // 0x02ef0700 + snd_hda_codec_write(codec, 0x2e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02e70700 +// snd_hda: 46 [] + + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 524); // 0x02ff0005 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 525); // 0x02ff0009 +// snd_hda: 47 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 526); // 0x02ff000f + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 527); // 0x02ff0012 +// snd_hda: amp capabilities 47 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 528); // 0x02ff000d +// snd_hda: amp capabilities 47 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 529); // 0x02ff0009 +// snd_hda: 47 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 530); // 0x02ff1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 531); // 0x02ff000c +// snd_hda: 47 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 532); // 0x02ff000e + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000d, 533); // 0x02ff0200 +// snd_hda: connection list 47 <- 13 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 534); // 0x02ff0700 + snd_hda_codec_write(codec, 0x2f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02f70700 +// snd_hda: 47 [] + + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 537); // 0x030f0005 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 538); // 0x030f0009 +// snd_hda: 48 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 539); // 0x030f000f + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 540); // 0x030f0012 +// snd_hda: amp capabilities 48 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 541); // 0x030f000d +// snd_hda: amp capabilities 48 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 542); // 0x030f0009 +// snd_hda: 48 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 543); // 0x030f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 544); // 0x030f000c +// snd_hda: 48 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 545); // 0x030f000e + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000e, 546); // 0x030f0200 +// snd_hda: connection list 48 <- 14 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 547); // 0x030f0700 + snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03070700 +// snd_hda: 48 [] + + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 550); // 0x031f0005 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 551); // 0x031f0009 +// snd_hda: 49 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 552); // 0x031f000f + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 553); // 0x031f0012 +// snd_hda: amp capabilities 49 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 554); // 0x031f000d +// snd_hda: amp capabilities 49 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 555); // 0x031f0009 +// snd_hda: 49 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 556); // 0x031f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 557); // 0x031f000c +// snd_hda: 49 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 558); // 0x031f000e + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000f, 559); // 0x031f0200 +// snd_hda: connection list 49 <- 15 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 560); // 0x031f0700 + snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03170700 +// snd_hda: 49 [] + + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 563); // 0x032f0005 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 564); // 0x032f0009 +// snd_hda: 50 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 565); // 0x032f000f + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 566); // 0x032f0012 +// snd_hda: amp capabilities 50 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 567); // 0x032f000d +// snd_hda: amp capabilities 50 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 568); // 0x032f0009 +// snd_hda: 50 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 569); // 0x032f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 570); // 0x032f000c +// snd_hda: 50 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 571); // 0x032f000e + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000010, 572); // 0x032f0200 +// snd_hda: connection list 50 <- 16 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 573); // 0x032f0700 + snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03270700 +// snd_hda: 50 [] + + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 576); // 0x033f0005 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 577); // 0x033f0009 +// snd_hda: 51 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 578); // 0x033f000f + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 579); // 0x033f0012 +// snd_hda: amp capabilities 51 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 580); // 0x033f000d +// snd_hda: amp capabilities 51 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 581); // 0x033f0009 +// snd_hda: 51 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 582); // 0x033f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 583); // 0x033f000c +// snd_hda: 51 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 584); // 0x033f000e + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000011, 585); // 0x033f0200 +// snd_hda: connection list 51 <- 17 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 586); // 0x033f0700 + snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03370700 +// snd_hda: 51 [] + + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 589); // 0x034f0005 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 590); // 0x034f0009 +// snd_hda: 52 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 591); // 0x034f000f + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 592); // 0x034f0012 +// snd_hda: amp capabilities 52 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 593); // 0x034f000d +// snd_hda: amp capabilities 52 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 594); // 0x034f0009 +// snd_hda: 52 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 595); // 0x034f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 596); // 0x034f000c +// snd_hda: 52 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 597); // 0x034f0700 + snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03470700 +// snd_hda: 52 [] + + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 600); // 0x035f0005 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 601); // 0x035f0009 +// snd_hda: 53 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 602); // 0x035f000f + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 603); // 0x035f0012 +// snd_hda: amp capabilities 53 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 604); // 0x035f000d +// snd_hda: amp capabilities 53 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 605); // 0x035f0009 +// snd_hda: 53 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 606); // 0x035f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 607); // 0x035f000c +// snd_hda: 53 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 608); // 0x035f0700 + snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03570700 +// snd_hda: 53 [] + + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 611); // 0x036f0005 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 612); // 0x036f0009 +// snd_hda: 54 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 613); // 0x036f000f + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 614); // 0x036f0012 +// snd_hda: amp capabilities 54 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 615); // 0x036f000d +// snd_hda: amp capabilities 54 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 616); // 0x036f0009 +// snd_hda: 54 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 617); // 0x036f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 618); // 0x036f000c +// snd_hda: 54 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 619); // 0x036f0700 + snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03670700 +// snd_hda: 54 [] + + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 622); // 0x037f0005 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 623); // 0x037f0009 +// snd_hda: 55 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 624); // 0x037f000f + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 625); // 0x037f0012 +// snd_hda: amp capabilities 55 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 626); // 0x037f000d +// snd_hda: amp capabilities 55 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 627); // 0x037f0009 +// snd_hda: 55 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 628); // 0x037f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 629); // 0x037f000c +// snd_hda: 55 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 630); // 0x037f0700 + snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03770700 +// snd_hda: 55 [] + + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 633); // 0x038f0005 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 634); // 0x038f0009 +// snd_hda: 56 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 635); // 0x038f000f + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 636); // 0x038f0012 +// snd_hda: amp capabilities 56 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 637); // 0x038f000d +// snd_hda: amp capabilities 56 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 638); // 0x038f0009 +// snd_hda: 56 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 639); // 0x038f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 640); // 0x038f000c +// snd_hda: 56 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 641); // 0x038f0700 + snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03870700 +// snd_hda: 56 [] + + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 644); // 0x039f0005 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 645); // 0x039f0009 +// snd_hda: 57 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 646); // 0x039f000f + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 647); // 0x039f0012 +// snd_hda: amp capabilities 57 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 648); // 0x039f000d +// snd_hda: amp capabilities 57 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 649); // 0x039f0009 +// snd_hda: 57 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 650); // 0x039f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 651); // 0x039f000c +// snd_hda: 57 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 652); // 0x039f0700 + snd_hda_codec_write(codec, 0x39, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03970700 +// snd_hda: 57 [] + + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 655); // 0x03af0005 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 656); // 0x03af0009 +// snd_hda: 58 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 657); // 0x03af000f + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 658); // 0x03af0012 +// snd_hda: amp capabilities 58 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 659); // 0x03af000d +// snd_hda: amp capabilities 58 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 660); // 0x03af0009 +// snd_hda: 58 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 661); // 0x03af1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 662); // 0x03af000c +// snd_hda: 58 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 663); // 0x03af0700 + snd_hda_codec_write(codec, 0x3a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03a70700 +// snd_hda: 58 [] + + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 666); // 0x03bf0005 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 667); // 0x03bf0009 +// snd_hda: 59 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 668); // 0x03bf000f + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 669); // 0x03bf0012 +// snd_hda: amp capabilities 59 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 670); // 0x03bf000d +// snd_hda: amp capabilities 59 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 671); // 0x03bf0009 +// snd_hda: 59 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 672); // 0x03bf1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 673); // 0x03bf000c +// snd_hda: 59 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 674); // 0x03bf0700 + snd_hda_codec_write(codec, 0x3b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03b70700 +// snd_hda: 59 [] + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 677); // 0x03cf0005 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 678); // 0x03cf0009 +// snd_hda: 60 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 679); // 0x03cf000f + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 680); // 0x03cf0012 +// snd_hda: amp capabilities 60 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 681); // 0x03cf000d +// snd_hda: amp capabilities 60 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 682); // 0x03cf0009 +// snd_hda: 60 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x00ab9030, 683); // 0x03cf1c00 +// snd_hda: pin config default 0x00ab9030 port conn 0 location 0x0 loc ext 0 loc geom 0 default device 10 conn type 11 color 9 misc 0 def assoc 3 seq 0 +// snd_hda: pin config default 0x00ab9030 port conn Jack loc ext Ext loc geom N/A default device Mic In conn type Combination color Pink misc undef def assoc 3 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 684); // 0x03cf000c +// snd_hda: 60 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 685); // 0x03cf0700 + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 688); // 0x03df0005 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 689); // 0x03df0009 +// snd_hda: 61 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 690); // 0x03df000f + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 691); // 0x03df0012 +// snd_hda: amp capabilities 61 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 692); // 0x03df000d +// snd_hda: amp capabilities 61 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 693); // 0x03df0009 +// snd_hda: 61 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 694); // 0x03df1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 695); // 0x03df000c +// snd_hda: 61 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 696); // 0x03df0700 + snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03d70700 +// snd_hda: 61 [] + + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 699); // 0x03ef0005 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 700); // 0x03ef0009 +// snd_hda: 62 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 701); // 0x03ef000f + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 702); // 0x03ef0012 +// snd_hda: amp capabilities 62 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 703); // 0x03ef000d +// snd_hda: amp capabilities 62 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 704); // 0x03ef0009 +// snd_hda: 62 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 705); // 0x03ef1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 706); // 0x03ef000c +// snd_hda: 62 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 707); // 0x03ef0700 + snd_hda_codec_write(codec, 0x3e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03e70700 +// snd_hda: 62 [] + + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 710); // 0x03ff0005 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 711); // 0x03ff0009 +// snd_hda: 63 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 712); // 0x03ff000f + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 713); // 0x03ff0012 +// snd_hda: amp capabilities 63 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 714); // 0x03ff000d +// snd_hda: amp capabilities 63 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 715); // 0x03ff0009 +// snd_hda: 63 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 716); // 0x03ff1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 717); // 0x03ff000c +// snd_hda: 63 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 718); // 0x03ff0700 + snd_hda_codec_write(codec, 0x3f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03f70700 +// snd_hda: 63 [] + + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 721); // 0x040f0005 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 722); // 0x040f0009 +// snd_hda: 64 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 723); // 0x040f000f + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 724); // 0x040f0012 +// snd_hda: amp capabilities 64 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 725); // 0x040f000d +// snd_hda: amp capabilities 64 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 726); // 0x040f0009 +// snd_hda: 64 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 727); // 0x040f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 728); // 0x040f000c +// snd_hda: 64 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 729); // 0x040f0700 + snd_hda_codec_write(codec, 0x40, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04070700 +// snd_hda: 64 [] + + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 732); // 0x041f0005 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 733); // 0x041f0009 +// snd_hda: 65 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 734); // 0x041f000f + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 735); // 0x041f0012 +// snd_hda: amp capabilities 65 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 736); // 0x041f000d +// snd_hda: amp capabilities 65 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 737); // 0x041f0009 +// snd_hda: 65 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 738); // 0x041f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 739); // 0x041f000c +// snd_hda: 65 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 740); // 0x041f0700 + snd_hda_codec_write(codec, 0x41, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04170700 +// snd_hda: 65 [] + + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 743); // 0x042f0005 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 744); // 0x042f0009 +// snd_hda: 66 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 745); // 0x042f000f + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 746); // 0x042f0012 +// snd_hda: amp capabilities 66 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 747); // 0x042f000d +// snd_hda: amp capabilities 66 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 748); // 0x042f0009 +// snd_hda: 66 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 749); // 0x042f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 750); // 0x042f000c +// snd_hda: 66 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 751); // 0x042f0700 + snd_hda_codec_write(codec, 0x42, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04270700 +// snd_hda: 66 [] + + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 754); // 0x043f0005 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 755); // 0x043f0009 +// snd_hda: 67 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 756); // 0x043f000f + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 757); // 0x043f0012 +// snd_hda: amp capabilities 67 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 758); // 0x043f000d +// snd_hda: amp capabilities 67 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 759); // 0x043f0009 +// snd_hda: 67 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 760); // 0x043f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 761); // 0x043f000c +// snd_hda: 67 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 762); // 0x043f0700 + snd_hda_codec_write(codec, 0x43, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04370700 +// snd_hda: 67 [] + + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 765); // 0x044f0005 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 766); // 0x044f0009 +// snd_hda: 68 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 767); // 0x044f000f + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 768); // 0x044f0012 +// snd_hda: amp capabilities 68 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 769); // 0x044f000d +// snd_hda: amp capabilities 68 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 770); // 0x044f0009 +// snd_hda: 68 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0xa0a60100, 771); // 0x044f1c00 +// snd_hda: pin config default 0xa0a60100 port conn 2 location 0x20 loc ext 2 loc geom 0 default device 10 conn type 6 color 0 misc 1 def assoc 0 seq 0 +// snd_hda: pin config default 0xa0a60100 port conn Fixed loc ext Sep loc geom N/A default device Mic In conn type Other Digital color Unknown misc Jack Detect Override def assoc 0 seq 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 772); // 0x044f000c +// snd_hda: 68 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 773); // 0x044f000d +// snd_hda: amp capabilities 68 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 774); // 0x044f0700 + snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 +// snd_hda: 68 [] + + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 777); // 0x045f0005 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 778); // 0x045f0009 +// snd_hda: 69 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 779); // 0x045f000f + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 780); // 0x045f0012 +// snd_hda: amp capabilities 69 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 781); // 0x045f000d +// snd_hda: amp capabilities 69 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 782); // 0x045f0009 +// snd_hda: 69 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 783); // 0x045f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 784); // 0x045f000c +// snd_hda: 69 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 785); // 0x045f000d +// snd_hda: amp capabilities 69 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 786); // 0x045f0700 + snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 +// snd_hda: 69 [] + + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 789); // 0x046f0005 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00700200, 790); // 0x046f0009 +// snd_hda: 70 AC_WID_BEEP ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_DIGITAL', 'AC_WCAP_TYPE'] 0 0 7 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 791); // 0x046f000f + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 792); // 0x046f0012 +// snd_hda: amp capabilities 70 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 793); // 0x046f000d +// snd_hda: amp capabilities 70 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00700200, 794); // 0x046f0009 +// snd_hda: 70 AC_WID_BEEP ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_DIGITAL', 'AC_WCAP_TYPE'] 0 0 7 + + snd_hda_codec_write(codec, 0x46, 0, AC_VERB_SET_BEEP_CONTROL, 0x00000000); // 0x04670a00 + +} + +static void read_vendor_node(struct hda_codec *codec) +{ + int retval; + + // I think this is also part of the node init loop + + // this may involve calls to AppleHDAWidgetCS8409::initForNodeID for each node + // and AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) for each node + + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 797); // 0x047f0005 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00f002c1, 798); // 0x047f0009 +// snd_hda: 71 AC_WID_VENDOR ['AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_UNSOL_CAP', 'AC_WCAP_DELAY', 'AC_WCAP_PROC_WID', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DIGITAL'] 0 0 15 + + // new addition June 2019 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 799); // 0x047f000f + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 800); // 0x047f0012 +// snd_hda: amp capabilities 71 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 801); // 0x047f000d +// snd_hda: amp capabilities 71 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00f002c1, 802); // 0x047f0009 +// snd_hda: 71 AC_WID_VENDOR ['AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_UNSOL_CAP', 'AC_WCAP_DELAY', 'AC_WCAP_PROC_WID', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DIGITAL'] 0 0 15 + +} + +static void read_coefs_all(struct hda_codec *codec); + +static void init_read_coefs(struct hda_codec *codec) +{ + + int retval; + + + // in AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) + + // get number of coefs in bits 15:8 - here 0x0000ff00 ie 255 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000010, 0x0000ff00, 803); // 0x047f0010 + + + // this is after the read_all_nodes loop + + read_coefs_all(codec); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void read_coefs_all(struct hda_codec *codec) +{ + + // leave these in as check of state + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 804 ); // coef read 804 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 808 ); // coef read 808 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000080, 812 ); // coef read 812 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0003, 0x0000, 0x0000801f, 816 ); // coef read 816 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0004, 0x0000, 0x0000083f, 820 ); // coef read 820 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0005, 0x0000, 0x00000000, 824 ); // coef read 824 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0006, 0x0000, 0x00008000, 828 ); // coef read 828 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0007, 0x0000, 0x000028ff, 832 ); // coef read 832 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0008, 0x0000, 0x00000000, 836 ); // coef read 836 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x00000013, 840 ); // coef read 840 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000a, 0x0000, 0x00000000, 844 ); // coef read 844 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000b, 0x0000, 0x00000000, 848 ); // coef read 848 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000c, 0x0000, 0x0000cccc, 852 ); // coef read 852 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000d, 0x0000, 0x0000cccc, 856 ); // coef read 856 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000e, 0x0000, 0x00000000, 860 ); // coef read 860 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000f, 0x0000, 0x0000aaaa, 864 ); // coef read 864 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0010, 0x0000, 0x0000cccc, 868 ); // coef read 868 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0011, 0x0000, 0x00000000, 872 ); // coef read 872 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0012, 0x0000, 0x0000cccc, 876 ); // coef read 876 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0013, 0x0000, 0x0000cccc, 880 ); // coef read 880 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0014, 0x0000, 0x00000000, 884 ); // coef read 884 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0015, 0x0000, 0x0000aaaa, 888 ); // coef read 888 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0016, 0x0000, 0x0000cccc, 892 ); // coef read 892 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0017, 0x0000, 0x00000000, 896 ); // coef read 896 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0018, 0x0000, 0x00000000, 900 ); // coef read 900 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000400, 904 ); // coef read 904 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00000420, 908 ); // coef read 908 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00008c20, 912 ); // coef read 912 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00008000, 916 ); // coef read 916 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001d, 0x0000, 0x00008c40, 920 ); // coef read 920 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001e, 0x0000, 0x00008000, 924 ); // coef read 924 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001f, 0x0000, 0x00008c60, 928 ); // coef read 928 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0020, 0x0000, 0x00008000, 932 ); // coef read 932 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0021, 0x0000, 0x00008000, 936 ); // coef read 936 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0022, 0x0000, 0x00008000, 940 ); // coef read 940 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0023, 0x0000, 0x00008000, 944 ); // coef read 944 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0024, 0x0000, 0x00008000, 948 ); // coef read 948 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0025, 0x0000, 0x00008000, 952 ); // coef read 952 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0026, 0x0000, 0x00008000, 956 ); // coef read 956 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0027, 0x0000, 0x00008000, 960 ); // coef read 960 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0028, 0x0000, 0x00008000, 964 ); // coef read 964 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00008000, 968 ); // coef read 968 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00008000, 972 ); // coef read 972 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002b, 0x0000, 0x00008000, 976 ); // coef read 976 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002c, 0x0000, 0x00008000, 980 ); // coef read 980 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002d, 0x0000, 0x00008000, 984 ); // coef read 984 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002e, 0x0000, 0x00008000, 988 ); // coef read 988 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002f, 0x0000, 0x00008000, 992 ); // coef read 992 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0030, 0x0000, 0x00008000, 996 ); // coef read 996 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0031, 0x0000, 0x00008000, 1000 ); // coef read 1000 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0032, 0x0000, 0x00008000, 1004 ); // coef read 1004 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0033, 0x0000, 0x00008000, 1008 ); // coef read 1008 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0034, 0x0000, 0x00008000, 1012 ); // coef read 1012 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0035, 0x0000, 0x00008000, 1016 ); // coef read 1016 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0036, 0x0000, 0x00008000, 1020 ); // coef read 1020 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0037, 0x0000, 0x00008000, 1024 ); // coef read 1024 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0038, 0x0000, 0x00008000, 1028 ); // coef read 1028 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0039, 0x0000, 0x00008000, 1032 ); // coef read 1032 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003a, 0x0000, 0x00008000, 1036 ); // coef read 1036 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003b, 0x0000, 0x00008000, 1040 ); // coef read 1040 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003c, 0x0000, 0x00008000, 1044 ); // coef read 1044 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003d, 0x0000, 0x00008000, 1048 ); // coef read 1048 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003e, 0x0000, 0x00008000, 1052 ); // coef read 1052 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003f, 0x0000, 0x00008000, 1056 ); // coef read 1056 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0040, 0x0000, 0x00008000, 1060 ); // coef read 1060 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0041, 0x0000, 0x00008000, 1064 ); // coef read 1064 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0042, 0x0000, 0x00008000, 1068 ); // coef read 1068 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0043, 0x0000, 0x00008000, 1072 ); // coef read 1072 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0044, 0x0000, 0x00008000, 1076 ); // coef read 1076 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0045, 0x0000, 0x00008000, 1080 ); // coef read 1080 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0046, 0x0000, 0x00008000, 1084 ); // coef read 1084 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0047, 0x0000, 0x00008000, 1088 ); // coef read 1088 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0048, 0x0000, 0x00008000, 1092 ); // coef read 1092 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008000, 1096 ); // coef read 1096 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008000, 1100 ); // coef read 1100 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004b, 0x0000, 0x00008000, 1104 ); // coef read 1104 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004c, 0x0000, 0x00008000, 1108 ); // coef read 1108 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004d, 0x0000, 0x00008000, 1112 ); // coef read 1112 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004e, 0x0000, 0x00008000, 1116 ); // coef read 1116 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004f, 0x0000, 0x00008000, 1120 ); // coef read 1120 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0050, 0x0000, 0x00008000, 1124 ); // coef read 1124 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0051, 0x0000, 0x00008000, 1128 ); // coef read 1128 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0052, 0x0000, 0x00008000, 1132 ); // coef read 1132 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0053, 0x0000, 0x00008000, 1136 ); // coef read 1136 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0054, 0x0000, 0x00008000, 1140 ); // coef read 1140 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0055, 0x0000, 0x00008000, 1144 ); // coef read 1144 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0056, 0x0000, 0x00008000, 1148 ); // coef read 1148 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0057, 0x0000, 0x00008000, 1152 ); // coef read 1152 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0058, 0x0000, 0x00000400, 1156 ); // coef read 1156 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0059, 0x0000, 0x00000074, 1160 ); // coef read 1160 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005a, 0x0000, 0x0000007f, 1164 ); // coef read 1164 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005b, 0x0000, 0x00000010, 1168 ); // coef read 1168 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005c, 0x0000, 0x00000020, 1172 ); // coef read 1172 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005d, 0x0000, 0x00002d7f, 1176 ); // coef read 1176 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005e, 0x0000, 0x00002d7f, 1180 ); // coef read 1180 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005f, 0x0000, 0x00001600, 1184 ); // coef read 1184 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0060, 0x0000, 0x00000000, 1188 ); // coef read 1188 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0061, 0x0000, 0x00000000, 1192 ); // coef read 1192 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0062, 0x0000, 0x00000000, 1196 ); // coef read 1196 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0063, 0x0000, 0x00000000, 1200 ); // coef read 1200 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0064, 0x0000, 0x00000000, 1204 ); // coef read 1204 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0065, 0x0000, 0x00000000, 1208 ); // coef read 1208 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0066, 0x0000, 0x00000000, 1212 ); // coef read 1212 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0067, 0x0000, 0x00000000, 1216 ); // coef read 1216 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0068, 0x0000, 0x00000000, 1220 ); // coef read 1220 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0069, 0x0000, 0x00000000, 1224 ); // coef read 1224 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006a, 0x0000, 0x00000000, 1228 ); // coef read 1228 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006b, 0x0000, 0x00000000, 1232 ); // coef read 1232 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006c, 0x0000, 0x00000000, 1236 ); // coef read 1236 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006d, 0x0000, 0x00000000, 1240 ); // coef read 1240 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006e, 0x0000, 0x00000000, 1244 ); // coef read 1244 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006f, 0x0000, 0x00000000, 1248 ); // coef read 1248 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0070, 0x0000, 0x00000000, 1252 ); // coef read 1252 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 1256 ); // coef read 1256 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0072, 0x0000, 0x00000000, 1260 ); // coef read 1260 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0073, 0x0000, 0x00000000, 1264 ); // coef read 1264 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0074, 0x0000, 0x00000000, 1268 ); // coef read 1268 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0075, 0x0000, 0x00000001, 1272 ); // coef read 1272 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0076, 0x0000, 0x00000009, 1276 ); // coef read 1276 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0077, 0x0000, 0x00000000, 1280 ); // coef read 1280 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0078, 0x0000, 0x00000000, 1284 ); // coef read 1284 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0079, 0x0000, 0x00000000, 1288 ); // coef read 1288 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007a, 0x0000, 0x00000000, 1292 ); // coef read 1292 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007b, 0x0000, 0x00000000, 1296 ); // coef read 1296 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007c, 0x0000, 0x00000000, 1300 ); // coef read 1300 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007d, 0x0000, 0x00000000, 1304 ); // coef read 1304 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007e, 0x0000, 0x00000000, 1308 ); // coef read 1308 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007f, 0x0000, 0x00000000, 1312 ); // coef read 1312 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0080, 0x0000, 0x00000000, 1316 ); // coef read 1316 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0081, 0x0000, 0x00000000, 1320 ); // coef read 1320 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000ff00, 1324 ); // coef read 1324 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void read_virtual_widgets(struct hda_codec *codec) +{ + // setup the virtual widgets + + unsigned int retval; + + mycodec_info(codec, "command nid start read_virtual_widgets\n"); + + // copied to outer routine + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // moved to outer routine + //snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1335); // 0x048f0005 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1336); // 0x048f0009 +// snd_hda: 72 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1337); // 0x048f000f + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1338); // 0x048f000a +// snd_hda: pcm params 72 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1339); // 0x048f000b +// snd_hda: stream format params 72 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1340); // 0x048f0012 +// snd_hda: amp capabilities 72 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1341); // 0x048f000d +// snd_hda: amp capabilities 72 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1342); // 0x048f0009 +// snd_hda: 72 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1344); // 0x049f0005 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1345); // 0x049f0009 +// snd_hda: 73 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1346); // 0x049f000f + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1347); // 0x049f000a +// snd_hda: pcm params 73 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1348); // 0x049f000b +// snd_hda: stream format params 73 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1349); // 0x049f0012 +// snd_hda: amp capabilities 73 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1350); // 0x049f000d +// snd_hda: amp capabilities 73 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1351); // 0x049f0009 +// snd_hda: 73 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1353); // 0x04af0005 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1354); // 0x04af0009 +// snd_hda: 74 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1355); // 0x04af000f + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1356); // 0x04af000a +// snd_hda: pcm params 74 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1357); // 0x04af000b +// snd_hda: stream format params 74 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1358); // 0x04af0012 +// snd_hda: amp capabilities 74 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1359); // 0x04af000d +// snd_hda: amp capabilities 74 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1360); // 0x04af0009 +// snd_hda: 74 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1362); // 0x04bf0005 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1363); // 0x04bf0009 +// snd_hda: 75 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1364); // 0x04bf000f + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1365); // 0x04bf000a +// snd_hda: pcm params 75 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1366); // 0x04bf000b +// snd_hda: stream format params 75 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1367); // 0x04bf0012 +// snd_hda: amp capabilities 75 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1368); // 0x04bf000d +// snd_hda: amp capabilities 75 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1369); // 0x04bf0009 +// snd_hda: 75 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + + + mycodec_info(codec, "command nid end read_virtual_widgets\n"); + +} + +static void setup_jack_pin_config(struct hda_codec *codec) +{ + + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // this is likely some call of setPinConfigDefault + // 0x45 -> 0x23 (macbook pro) is the line in path - so why does it say its a mike?? + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, 0x00000001); // 0x04571c01 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0x00000001); // 0x04571d01 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x000000a0); // 0x04571ea0 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x00000090); // 0x04571f90 +// snd_hda: pin config 0x90a00101 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 10 conn type 0 color 0 misc 1 def assoc 0 seq 1 +// snd_hda: pin config 0x90a00101 port conn Fixed loc ext Int loc geom N/A default device Mic In conn type Unknown color Unknown misc Jack Detect Override def assoc 0 seq 1 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void enable_i2c(struct hda_codec *codec) +{ + //int retval; + + // AppleBusControllerCS8409::init + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + +// snd_hda: # AppleHDAFunctionGroupCS8409::enableI2CClock: + // codes from windows cs4208_38.inf file + // 0x9008 = PLL1_EN(0x1000),I2C_EN(0x8) + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1691 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1691 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9008, 0x00000000, 1695 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1695 + + // 0x0002 0x0080 +GPIO_I2C (0x80) not used ASP1_BUS_IDLE = 0x200 ASP2_BUS_IDLE = 0x800 (0b1010) ASP1/2_BUS_IDLE = 10 + // 0x005b undocumented + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000000, 1699 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1699 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0002, 0x0080, 0x00000000, 1703 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1703 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000080, 1707 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1707 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005b, 0x0000, 0x00000010, 1711 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1711 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x005b, 0x0010, 0x00000000, 1715 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1715 + +// snd_hda: # AppleHDAFunctionGroupCS8409::enableI2CClock: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1719 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1719 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9000, 0x00000000, 1723 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1723 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void init_for_node_vendor(struct hda_codec *codec) +{ + //int retval; + int coef_idx = 0x1a; + //int coef_ret = 0; + + // this is AppleHDAFunctionGroupCS8409::initForNodeID + + mycodec_info(codec, "command init_for_node_vendor start\n"); + + // these 2 items seem to enable the i2c clock - but we havent enabled i2c yet!! + // whats the difference?? + // Im certain this is enabling the i2c clock + // codes from windows cs4208_38.inf file + // 0x9008 = PLL1_EN(0x1000),I2C_EN(0x8) + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1371 ); // coef read 1371 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9008, 0x00000000, 1375 ); // coef write 1375 + + // clear these coefs + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 1379 ); // coef read 1379 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 1383 ); // coef write 1383 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0017, 0x0000, 0x00000000, 1387 ); // coef write 1387 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0018, 0x0000, 0x00000000, 1391 ); // coef write 1391 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000000, 1395 ); // coef write 1395 + + //coef_ret = cs_8409_vendor_coef_set(codec, 0x19, 0x8000); + cs_8409_vendor_coef_set(codec, 0x19, 0x8000); + + //if (coef_ret == -1) error; + + while (coef_idx <= 0x57) + { + //coef_ret = cs_8409_vendor_coef_set(codec, coef_idx, 0x8000); + cs_8409_vendor_coef_set(codec, coef_idx, 0x8000); + + //if (coef_ret == -1) error; + + coef_idx++; + } + + // 0x82 0x0000 ASP1/2_xxx_EN = 0, DMIC1/2_SCL_EN = 0 + // others not documented cs4208_38.inf + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x000a, 0x0000, 0x00000000, 1651 ); // coef write 1651 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00000000, 1655 ); // coef write 1655 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 1659 ); // coef write 1659 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 1663 ); // coef write 1663 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00000000, 1667 ); // coef write 1667 + + + mycodec_info(codec, "command init_for_node_vendor end\n"); + +} + +static void determine_speaker_id(struct hda_codec *codec) +{ + int retval; + + // this is determineSpeakerID + // this does not make sense - this just checks a GPIO pin?? + + mycodec_info(codec, "command determine_speaker_id start\n"); + + // this is call AppleHDAFunctionGroup::setGPIOEnable in determineSpeakerID + + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000008); // 0x00171608 +// snd_hda: gpio enable 1 0x08 + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000000, 1672); // 0x001f1500 +// snd_hda: gpio data 1 0x00 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000000c); // 0x0017160c +// snd_hda: gpio enable 1 0x0c + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000000, 1674); // 0x001f1500 +// snd_hda: gpio data 1 0x00 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000008); // 0x00171608 + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0x8 data 0x%08x\n", retval); + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000000c); // 0x0017160c + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0xc data 0x%08x\n", retval); + + } + else + { + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000004); // 0x00171604 +// snd_hda: gpio enable 1 0x04 + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000004, 1672); // 0x001f1500 +// snd_hda: gpio data 1 0x04 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x04); // 0x00171604 + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0x4 data 0x%08x\n", retval); + + } + + mycodec_info(codec, "command determine_speaker_id end\n"); + +} + + +static void enable_GPIforUR(struct hda_codec *codec, int mask) +{ + int retval; + + + // AppleHDAFunctionGroupCS8409::enableGPIforUR + + mycodec_info(codec, "command enable_GPIforUR start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + // this is AppleHDAFunctionGroup::setGPIOEnable in AppleHDAFunctionGroup::enableGPIforUR + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000005); // 0x00171605 +// snd_hda: gpio enable 1 0x05 + + // this is AppleHDAFunctionGroup::setGPIODirection in AppleHDAFunctionGroup::enableGPIforUR + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000000); // 0x00171700 +// snd_hda: gpio direction 1 0x00 in in in in in in in in + + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_WAKE_MASK, 0x00000000, 0x00000000, 1735); // 0x001f1800 +// snd_hda: gpio wake enable 1 0x00 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_WAKE_MASK, 0x00000001); // 0x00171801 +// snd_hda: gpio wake enable 1 0x01 + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0x00000000, 0x00000000, 1737); // 0x001f1900 +// snd_hda: gpio unsol enable 1 0x00 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x00000001); // 0x00171901 +// snd_hda: gpio unsol enable 1 0x01 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171605 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000000); // 0x00171700 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_WAKE_MASK, 0x00000000, 0x00000000, 1735); // 0x001f1800 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_WAKE_MASK, 0x00000001); // 0x00171801 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0x00000000, 0x00000000, 1737); // 0x001f1900 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x00000001); // 0x00171901 + + + // likely last runVerb of AppleHDAFunctionGroupCS8409::enableGPIforUR + snd_hda_codec_write(codec, codec->core.afg, 0, 0x7f0, 0x000000b7); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command enable_GPIforUR end\n"); + +} + + // this may be more likely + // only issue is that AppleHDATDM_Codec::resetDevice seems to have other functions that seem likely to send i2c data + // note the setExternalControlForDefaultDeviceTypeAndTag function sends the GPIO updates as noted above + // but AppleHDATDM_CS42L83::resetDevice contains the 0x1301 to 0x130f sequence + // AppleHDATDM_CS42L83::init + // AppleHDATDM_CS42L83::resetDevice + // AppleHDATDM_Codec::resetDevice + // AppleHDAFunctionGroup::externalControlExistsForDefaultDeviceTypeAndTag(0xf(%rsi), 0x35(%rdx)) + // AppleHDAFunctionGroup::setExternalControlForDefaultDeviceTypeAndTag(1(%rsi), 0xf(%rdx), 0x35(%rcx)) + // AppleHDAFunctionGroupExternalControl::setExternalControlState(unk(%rsi), unk(%rdx)) + // AppleHDAFunctionGroup::setExternalControlForDefaultDeviceTypeAndTag(0(%rsi), 0xf(%rdx), 0x35(%rcx)) + // AppleHDAFunctionGroupExternalControl::setExternalControlState(unk(%rsi), unk(%rdx)) + + // AppleHDAFunctionGroupExternalControl::setExternalControlState + // calls AppleHDAFunctionGroupExternalControl::publicSetExternalControlState + // this appears to be virtual - the definition just asserts + // good candidate is AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState + // AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState + // is the function which does the external_control_GPIO_clear0 or external_control_GPIO_set1 + // functions below + + // followed by i2c calls for 0x1301 to 0x130f + // we have a 2nd set of GPIO clear/set calls which cant figure out where they get called + // followed by i2c calls 0x1001 + // followed by i2c calls 0x1005 + // followed by AppleHDATDM_CS42L83::_initHW() + + +static void cs42l83_external_control_GPIO_clear_2(struct hda_codec *codec,int mask); +static void cs42l83_external_control_GPIO_set_2(struct hda_codec *codec,int mask); + +static void cs42l83_external_control_GPIO(struct hda_codec *codec,int mask) +{ + + // from AppleHDATDM_Codec::resetDevice + + // I think this is resetting the device by pulling some line low then high + // GPIO 2 is for the cs42l83 + + // note that AppleHDAFunctionGroupExternalControl::setExternalControlState + // has an IOSleep() as the first call depending on a value - if non-zero time to sleep + // if 0 IOSleep() call ignored + + mycodec_info(codec, "command cs42l83_external_control_GPIO start\n"); + + // this clearing then setting gpio bit 2 + + //usleep_range(2000,4000); + + cs42l83_external_control_GPIO_clear_2(codec,mask); + + //usleep_range(2000,4000); + + cs42l83_external_control_GPIO_set_2(codec,mask); + + + mycodec_info(codec, "command cs42l83_external_control_GPIO end\n"); + +} + + +static void cs42l83_external_control_GPIO_clear_2(struct hda_codec *codec,int mask) +{ + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // AppleHDAFunctionGroup::setGPIODirection + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 +// snd_hda: gpio direction 1 0x02 in in in in in in out in + // AppleHDAFunctionGroup::setGPIOData + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000000); // 0x00171500 +// snd_hda: gpio data 1 0x00 + // AppleHDAFunctionGroup::setGPIOEnable + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000007); // 0x00171607 +// snd_hda: gpio enable 1 0x07 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000000); // 0x00171500 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171607 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void cs42l83_external_control_GPIO_set_2(struct hda_codec *codec,int mask) +{ + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // AppleHDAFunctionGroup::setGPIODirection + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 +// snd_hda: gpio direction 1 0x02 in in in in in in out in + // AppleHDAFunctionGroup::setGPIOData + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000002); // 0x00171502 +// snd_hda: gpio data 1 0x02 + // AppleHDAFunctionGroup::setGPIOEnable + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000007); // 0x00171607 +// snd_hda: gpio enable 1 0x07 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000002); // 0x00171502 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171607 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void cs42l83_reset(struct hda_codec *codec) +{ + // AppleHDATDM_CS42L83::resetDevice() + + // these are i2c calls + // so it seems Apple is using SET_COEF_INDEX, SET_PROC_COEF to write to the local i2c bus + // writing to coef index 0x59 for the i2c address, reading from 0x5c for i2c status + // and writing to 0x5d for i2c register, data writes (8 bit each in the 16 bit coef data with register in hi byte) + // and writing/reading to 0x5e for i2c register, data reads - the write is register with lo byte 0x00 + // and read is data for i2c register (i2c register returned) with data in lo byte + + // based on info from bugs 195671 and 110561 + // and that writing to coef index 0x59 seems to be the i2c address hence the i2c address is 0x90 + // this is likely setting up the headphone/mic jack ic CS42L83A + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_reset end\n"); +} + +static int cs42l83_device_id(struct hda_codec *codec) +{ + int retval_dev; + int retval_rev; + int ret_id; + + // in AppleHDATDM_CS42L83::init + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0100 i2c data 0x0142 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0500 i2c data 0x05b0 + + retval_dev = cs_8409_vendor_i2cRead(codec, 0x90, 0x1001, 1); // snd_hda + retval_rev = cs_8409_vendor_i2cRead(codec, 0x90, 0x1005, 1); // snd_hda + + ret_id = ((retval_dev & 0xff) << 16) | (retval_rev & 0xff); + + mycodec_info(codec, "command cs42l83_device_id end\n"); + + return ret_id; +} + +static void cs42l83_inithw(struct hda_codec *codec) +{ + + // AppleHDATDM_CS42L83::_initHW() called from AppleHDATDM_CS42L83::init + + // this triggers the 1st UNSOL response + + mycodec_info(codec, "command cs42l83_inithw start\n"); + + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0300 i2c data 0x0320 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0322 i2c data 0x0022 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2901 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x200f i2c data 0x000f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b03 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x023f i2c data 0x003f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x21a6 i2c data 0x00a6 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1600 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1800 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1900 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1a00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1c00 i2c data 0x1c7f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1c1a i2c data 0x001a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1e00 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1f00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x01fe i2c data 0x00fe +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x751f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x759f i2c data 0x009f + + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1103, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1103, 0x0022, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1129, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1320, 0x000f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2302, 0x003f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f01, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1121, 0x00a6, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1316, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1317, 0x0000, 1); // snd_hda + + // this triggers an UNSOL response with interrupt cleared state + // this writes to SRC Interrupt Mask unmasking all interrupts + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1318, 0x0000, 1); // snd_hda + + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1319, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131a, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131c, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131c, 0x001a, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131e, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131f, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + // the following is setting the power register + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0xff to 0xfe (Codec power up) + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x00fe, 1); // snd_hda + + // the following is setting the mic detect register + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x1f ((0x80 LATCH_TO_VP unset, HS_DETECT_LEVEL 1f) to 0x9f (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x1f) + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x009f, 1); // snd_hda + + + mycodec_info(codec, "command cs42l83_inithw end\n"); +} + +static void setup_gpio_set_20(struct hda_codec *codec); + +static void setup_amps_reset_i2c_max(struct hda_codec *codec) +{ + // based on info from bugs 195671 and 110561 + // and that writing to coef index 0x59 seems to be the i2c address hence the i2c address following are + // the amps 62, 64, 72, 74 + // also the data being sent does correpond to the registers of the MAX98372 from data sheet + // which is supposedly similar to the actual MAX98706 + // all analysis of the i2cWrite data is based on the MAX98372 data sheet + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x64, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x62 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x62, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x74 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x74, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x72 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x72, 0x0051, 0x0001, 0); // snd_hda + +} + +static void setup_gpio_set_20(struct hda_codec *codec) +{ + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // I think this may be associated with the amps + // - this is a difference between the MBP 13,1 and SSM amps and 14,3 and MAX amps + // its either applying power to the amps or taking them out of reset + // (note that unlike GPIO 2 there doesnt seem to be a clear 20) + + mycodec_info(codec, "command setup_gpio_set_20 start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000022); // 0x00171722 +// snd_hda: gpio direction 1 0x22 in in out in in in out in + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000022); // 0x00171522 +// snd_hda: gpio data 1 0x22 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000027); // 0x00171627 +// snd_hda: gpio enable 1 0x27 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000022); // 0x00171722 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000022); // 0x00171522 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000027); // 0x00171627 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command setup_gpio_set_20 end\n"); +} + + +static void setup_amps_reset_i2c_ssm3(struct hda_codec *codec) +{ + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x28 i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2a i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2c i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2e i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled + + cs_8409_vendor_i2cWrite(codec, 0x28, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2a, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2c, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2e, 0x0000, 0x0083, 1); // snd_hda + +} + + +static void setup_gpio_set_10(struct hda_codec *codec); + +static void setup_amps_reset_i2c_tas576(struct hda_codec *codec) +{ + // the Texas TAS5764 is undocumented - the nearest seems to be the TAS5760md + // which has some similarities but some discrepancies + // for the moment using the TAS5760md registers where they seem to be similar + // based on info that writing to coef index 0x59 seems to be the i2c address hence the amps i2c address following are + // 0xd8, 0xda, 0xdc, 0xde + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xda i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xda i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xda i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xda i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xda i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xda i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xde i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xde i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xde i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xde i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xde i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xde i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0014, 0x0002, 0); // snd_hda + +} + +static void setup_gpio_set_10(struct hda_codec *codec) +{ + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // I think this may be associated with the amps + // its either applying power to the amps or taking them out of reset + // (note that unlike GPIO 2 there doesnt seem to be a clear 10) + + // note that this seems to be following the MAX style setup + + mycodec_info(codec, "command setup_gpio_set_10 start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000012); // 0x00171712 +// snd_hda: gpio direction 1 0x12 in in in out in in out in + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000012); // 0x00171512 +// snd_hda: gpio data 1 0x12 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000001f); // 0x0017161f +// snd_hda: gpio enable 1 0x1f + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000012); // 0x00171712 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000012); // 0x00171512 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000001f); // 0x0017161f + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command setup_gpio_set_10 end\n"); +} + + +static void cs42l83_mic_detect(struct hda_codec *codec) +{ + int retval; + int newval; + + int detect_standby = 0; + + // likely from AppleHDAMikeyInternalCS8409::setupJackDetection + // which does call enterStandby with 0 arg + + // enterStandby is only function that issues 0x75 but it doesnt look right + // mic detect is register name + + // AppleHDATDM_CS42L83::enterStandby + + // so this appears to change 0x1b75 from 0x9f to 0x9f + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) to 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // curious - these functions not separated by power down/ups + // ah - the log may not be ordered - they are output as a bunch at the end!! + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x759f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x759f i2c data 0x009f + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + + if (detect_standby) + newval = (retval & 0x7f); + else + newval = (retval | 0x80); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x009f, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_mic_detect end\n"); +} + +static void cs42l83_tip_sense(struct hda_codec *codec, int invert) +{ + int retval; + int newval1; + int newval2; + int newval; + //int invert = 0; + + // likely in AppleHDAMikeyInternalCS8409::setupJackDetection + // - only 0x73 readMikey/writeMikey calls seen + + // register 0x1b73 - Tip Sense Control 2 + // changed from 0x02 (default) to 0xc0 (short detect) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7300 i2c data 0x7302 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x73c0 i2c data 0x00c0 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b73, 1); // snd_hda + + // invert indicates inverted signal path for physical jack presence detect circuit + + // following code translated from assembler + newval1 = (retval & 0x1c); + if (invert) + newval = (newval1 | 0xe0); + else + newval = (newval1 | 0xc0); + + + if (invert) + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b73, 0x00e0, 1); // snd_hda + else + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b73, 0x00c0, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_tip_sense end\n"); +} + + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h new file mode 100644 index 0000000..17b3ffd --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h @@ -0,0 +1,432 @@ + +/* playback mute control with the software mute bit check */ +static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + if (spec->auto_mute_via_amp) { + hda_nid_t nid = get_amp_nid(kcontrol); + bool enabled = !((spec->mute_bits >> nid) & 1); + ucontrol->value.integer.value[0] &= enabled; + ucontrol->value.integer.value[1] &= enabled; + } +} + + +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} + + +/* + * Bound mute controls + */ +#define AMP_VAL_IDX_SHIFT 19 +#define AMP_VAL_IDX_MASK (0x0f<<19) + +static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} + +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int i, indices, err = 0, change = 0; + + sync_auto_mute_bits(kcontrol, ucontrol); + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; + for (i = 0; i < indices; i++) { + kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | + (i << AMP_VAL_IDX_SHIFT); + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err < 0) + break; + change |= err; + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err < 0 ? err : change; +} + + +enum { + HDA_CTL_WIDGET_VOL, + HDA_CTL_WIDGET_MUTE, + HDA_CTL_BIND_MUTE, +}; + + +static const struct snd_kcontrol_new control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + /* only the put callback is replaced for handling the special mute */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = hda_gen_mixer_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_hda_mixer_amp_switch_info, + .get = hda_gen_bind_mute_get, + .put = hda_gen_bind_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, +}; + + +/* add the powersave loopback-list entry */ +static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + list = snd_array_new(&spec->loopback_list); + if (!list) + return -ENOMEM; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->loopback.amplist = spec->loopback_list.list; + return 0; +} + + +static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + + + +//static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) +//{ +// struct hda_gen_spec *spec = codec->spec; +// struct snd_kcontrol_new *knew; +// char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +// unsigned int defcfg; +// +// if (pin == spec->hp_mic_pin) +// return 0; /* already done in create_out_jack_mode() */ +// +// /* no jack mode for fixed pins */ +// defcfg = snd_hda_codec_get_pincfg(codec, pin); +// if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) +// return 0; +// +// /* no multiple vref caps? */ +// if (get_in_jack_num_items(codec, pin) <= 1) +// return 0; +// +// get_jack_mode_name(codec, pin, name, sizeof(name)); +// knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); +// if (!knew) +// return -ENOMEM; +// knew->private_value = pin; +// return 0; +//} + + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path) +{ + char buf[40]; + char *pos = buf; + int i; + + *pos = 0; + for (i = 0; i < path->depth; i++) + pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", + pos != buf ? ":" : "", + path->path[i]); + + codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); +} + + +/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +} + + +/* fill the label for each input at first */ +static int fill_input_pin_labels(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *label; + int j, idx; + + if (!is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + idx = 0; + for (j = i - 1; j >= 0; j--) { + if (spec->input_labels[j] && + !strcmp(spec->input_labels[j], label)) { + idx = spec->input_label_idxs[j] + 1; + break; + } + } + + spec->input_labels[i] = label; + spec->input_label_idxs[i] = idx; + } + + return 0; +} + +/* Parse the codec tree and retrieve ADCs */ +static int fill_adc_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int nums = 0; + + for_each_hda_codec_node(nid, codec) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + if (++nums >= max_nums) + break; + } + spec->num_adc_nids = nums; + + codec_dbg(codec, "fill_adc_nids num nids %d\n",nums); + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = nums; + memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); + + return nums; +} + + +#define update_pin_ctl(codec, pin, val) \ + snd_hda_codec_write_cache(codec, pin, 0, \ + AC_VERB_SET_PIN_WIDGET_CONTROL, val) + +/* set the pinctl target value and write it if requested */ +static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool do_write) +{ + if (!pin) + return; + val = snd_hda_correct_pin_ctl(codec, pin, val); + snd_hda_codec_set_pin_target(codec, pin, val); + if (do_write) + update_pin_ctl(codec, pin, val); +} + + + +/* nid, dir and idx */ +#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) + +/* check whether the given ctl is already assigned in any path elements */ +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) +{ + struct hda_gen_spec *spec = codec->spec; + const struct nid_path *path; + int i; + + val &= AMP_VAL_COMPARE_MASK; + snd_array_for_each(&spec->paths, i, path) { + if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) + return true; + } + return false; +} + +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int type) +{ + unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + return is_ctl_used(codec, val, type); +} + + +/* return true if either a volume or a mute amp is found for the given + * aamix path; the amp has to be either in the mixer node or its direct leaf + */ +static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, + hda_nid_t pin, unsigned int *mix_val, + unsigned int *mute_val) +{ + int idx, num_conns; + const hda_nid_t *list; + hda_nid_t nid; + + idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); + if (idx < 0) + return false; + + *mix_val = *mute_val = 0; + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) + *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) + *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + if (*mix_val && *mute_val) + return true; + + /* check leaf node */ + num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); + if (num_conns < idx) + return false; + nid = list[idx]; + if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && + !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) + *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && + !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) + *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + + return *mix_val || *mute_val; +} + + + + +/* add dynamic controls from template */ +static struct snd_kcontrol_new * +add_control(struct hda_gen_spec *spec, int type, const char *name, + int cidx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); + if (!knew) + return NULL; + knew->index = cidx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + if (knew->access == 0) + knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + knew->private_value = val; + return knew; +} + + +static int add_control_with_pfx(struct hda_gen_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, int cidx, unsigned long val) +{ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int len; + + len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + if (snd_BUG_ON(len >= sizeof(name))) + return -EINVAL; + if (!add_control(spec, type, name, cidx, val)) + return -ENOMEM; + return 0; +} + + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) + + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct hda_codec *codec, int input_idx, + hda_nid_t pin, const char *ctlname, int ctlidx, + hda_nid_t mix_nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int mix_val, mute_val; + int err, idx; + + if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) + return 0; + + path = snd_hda_add_new_path(codec, pin, mix_nid, 0); + if (!path) + return -EINVAL; + print_nid_path(codec, "loopback", path); + spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); + + idx = path->idx[path->depth - 1]; + if (mix_val) { + err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); + if (err < 0) + return err; + path->ctls[NID_PATH_VOL_CTL] = mix_val; + } + + if (mute_val) { + err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); + if (err < 0) + return err; + path->ctls[NID_PATH_MUTE_CTL] = mute_val; + } + + path->active = true; + path->stream_enabled = true; /* no DAC/ADC involved */ + err = add_loopback_list(spec, mix_nid, idx); + if (err < 0) + return err; + + if (spec->mixer_nid != spec->mixer_merge_nid && + !spec->loopback_merge_path) { + path = snd_hda_add_new_path(codec, spec->mixer_nid, + spec->mixer_merge_nid, 0); + if (path) { + print_nid_path(codec, "loopback-merge", path); + path->active = true; + path->pin_fixed = true; /* static route */ + path->stream_enabled = true; /* no DAC/ADC involved */ + spec->loopback_merge_path = + snd_hda_get_path_idx(codec, path); + } + } + + return 0; +} + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h new file mode 100644 index 0000000..1eeb183 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h @@ -0,0 +1,1906 @@ + + +// this sets the power state of the AFG node - ie node 0x1 +// this calls hda_sync_power_state +//hda_set_power_state(codec, AC_PWRST_D0); + +// this checks the node has reached the requested power state +//state = hda_sync_power_state(codec, nid, power_state); +// + + +// pigs need local definition as this is a static local function + +/* + * wait until the state is reached, returns the current state + */ +static unsigned int hda_sync_power_state_8409(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + unsigned long end_time = jiffies + msecs_to_jiffies(500); + unsigned int state, actual_state; + mycodec_info(codec, "hda_sync_power_state_8409 to 0x%04x\n",power_state); + + for (;;) { + state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state & AC_PWRST_ERROR) + break; + actual_state = (state >> 4) & 0x0f; + if (actual_state == power_state) + break; + if (time_after_eq(jiffies, end_time)) + break; + /* wait until the codec reachs to the target state */ + msleep(1); + } + mycodec_info(codec, "hda_sync_power_state_8409 power state 0x%04x\n",state); + return state; +} + +// pigs - need my own power state +// Apple seems to set node 0x01 - the AFG - primarily +// hda_set_power_state sets all nodes to the required power state +// so apparently node 0x01 does not have the power capability - but is powerable!! +// if we wish to use this for all nodes then need to check for this + +static unsigned int hda_set_node_power_state_dbg(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state, bool dbgflg) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int state = power_state; + //unsigned int current_state; + if (dbgflg) mycodec_info(codec, "hda_set_node_power_state nid 0x%02x power %d\n",nid,power_state); + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + if (!(state & AC_PWRST_ERROR)) { + if (state != power_state) { + if (nid == 0x01 || (wcaps & AC_WCAP_POWER)) { + if (nid != 0x01 && codec->power_filter) { + state = codec->power_filter(codec, nid, power_state); + // ah - this is for preventing a node from being turned off + // we are not in AC_PWRST_D3 but we are requesting AC_PWRST_D3 + // (Im assuming we assume if not in AC_PWRST_D3 we are in AC_PWRST_D0 + if (state != power_state && power_state == AC_PWRST_D3) + {} + else + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + } + else + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + + state = hda_sync_power_state_8409(codec, nid, power_state); + } + else + dev_info(hda_codec_dev(codec), "hda_set_node_power_state no power cap!!\n"); + } + } + else { + dev_info(hda_codec_dev(codec), "hda_set_node_power_state ERROR!! nid 0x%02x 0x%04x\n",nid, state); + } + if (dbgflg) mycodec_info(codec, "hda_set_node_power_state end power %d\n",state); + + return state; +} + +static unsigned int hda_set_node_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) +{ + return hda_set_node_power_state_dbg(codec, nid, power_state, 0); +} + +static unsigned int hda_set_node_power_state_simple(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) +{ + unsigned int state = power_state; + //unsigned int current_state; + mycodec_info(codec, "hda_set_node_power_state_simple power %d\n",power_state); + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + if (!(state & AC_PWRST_ERROR)) { + if (state != power_state) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + state = hda_sync_power_state_8409(codec, nid, power_state); + } + } + mycodec_info(codec, "hda_set_node_power_state_simple end power %d\n",state); + + return state; +} + + +static void hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, int flagint) +{ + unsigned int state; + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + mycodec_info(codec, "hda_check_power_state nid 0x%02x power 0x%04x %d\n", nid, state, flagint); +} + + +// go with Apple way?? +// this always does a get with index 0 initially and terminates with a set to 0 finally + +static inline unsigned int cs_8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + struct cs8409_apple_spec *spec = codec->spec; + unsigned int retval; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + retval = snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + return retval; +} + +static inline void cs_8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + struct cs8409_apple_spec *spec = codec->spec; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_PROC_COEF, coef); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + // appears to return 0 +} + +static inline unsigned int cs_8409_vendor_coef_set_mask(struct hda_codec *codec, unsigned int idx, + unsigned int coef, unsigned int mask, unsigned int srcval, int srcidx) +{ + // for the moment hackily add srcidx argument while debugging + struct cs8409_apple_spec *spec = codec->spec; + unsigned int retval; + unsigned int mask_coef; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + retval = snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + mask_coef = (retval & ~mask) | coef; + if (srcval != 0) + { + if (srcidx != 0 && mask_coef != srcval) + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) 0x%04x != 0x%04x %d BAD",idx,coef,mask_coef,retval,coef,mask, mask_coef, srcval, srcidx); + else + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",idx,coef,mask_coef,retval,coef,mask,srcidx); + } + else + //if (mask != 0xffff) + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",idx,coef,mask_coef,retval,coef,mask,srcidx); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_PROC_COEF, mask_coef); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + // appears to return 0 + // lets return the read value for checking + return retval; +} + +static inline void cs_8409_vendor_enableI2Cclock(struct hda_codec *codec, unsigned int flag) +{ + + unsigned int retval = 0; + unsigned int newval = 0; + + // note that apple returns the status value with data value in returned parameter + // snd_hda_codec_read just returns value - not sure what happens about errors + // looks as tho its assumed -1 is not a valid return value + // ah yes - because max val is 16 bit quantity + + retval = cs_8409_vendor_coef_get(codec, 0x0); + //if (retval == -1) + + if (retval == -1) + return; + + newval = retval; + if (flag) + newval |= 0x8; + else + newval = (retval & 0xfffffff7); + + cs_8409_vendor_coef_set(codec, 0x0, newval); + +} + + +// define i2cRead and i2cWrite functions +// following Apple +static unsigned int cs_8409_vendor_i2cRead(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int paged) +{ + // AppleHDAFunctionGroupCS8409::_i2cRead(bool, unsigned short, unsigned short, unsigned int*) + // note that last argument is return data + unsigned int i2c_reg_data; + unsigned int retval; + int rdcnt; + + myprintk_dbg("snd_hda_intel: i2cRead 0x%04x 0x%04x: %d",i2c_address,i2c_reg,paged); + + hda_set_node_power_state_dbg(codec, codec->core.afg, AC_PWRST_D0, 0); + // exit on error + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); + // exit on error + + cs_8409_vendor_enableI2Cclock(codec, 0x1); + + + cs_8409_vendor_coef_set(codec, 0x59, i2c_address); + + if (paged) + { + unsigned int retval1; + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg >> 8); + + rdcnt = -8; +sleep1: + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + if (retval1 != -1) + { + retval1 &= 0x18; + if (retval1 != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep1; + } + } + } + } + + + // so the i2c register is stored in the low byte of i2c_reg + // shift it 8 bits to left for sending as coefficient data (16 bits) + // hmm - why do I need a mask?? + // think either we mask here or in cs_8409_vendor_coef_set + // Apple is using short ints so likely automasked + i2c_reg_data = (i2c_reg << 8) & 0x0ffff; + + cs_8409_vendor_coef_set(codec, 0x5e, i2c_reg_data); + //if (retval == -1) + + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + rdcnt = -8; +sleep2: + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + if (retval != -1) + { + retval &= 0x18; + if (retval != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep2; + } + } + } + + // well thats interesting - looks as though the 16 bit return + // has the register in bits 15-8 and the data in 7-0 + // probably should mask the data out + retval = cs_8409_vendor_coef_get(codec, 0x5e); + //if (retval == -1) + + cs_8409_vendor_enableI2Cclock(codec, 0x0); + // exit on error + + myprintk_dbg("snd_hda_intel: i2cRead 0x%04x 0x%04x: 0x%04x end",i2c_address,i2c_reg,retval); + + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + // exit on error + + return retval; + +} + +static unsigned int cs_8409_vendor_i2cWrite(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int i2c_data, unsigned int paged) +{ + // AppleHDAFunctionGroupCS8409::_i2cWrite(bool, unsigned short, unsigned short, unsigned short) + unsigned int retval; + unsigned int i2c_reg_data; + int rdcnt; + + myprintk_dbg("snd_hda_intel: i2cWrite 0x%04x 0x%04x: 0x%04x %d",i2c_address,i2c_reg,i2c_data,paged); + + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + // exit on error + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); + // exit on error + + cs_8409_vendor_enableI2Cclock(codec, 0x1); + + + cs_8409_vendor_coef_set(codec, 0x59, i2c_address); + + if (paged) + { + unsigned int retval1; + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg >> 8); + + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + rdcnt = -8; +sleep1: + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + if (retval1 != -1) + { + retval1 &= 0x18; + if (retval1 != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep1; + } + } + } + } + + + // so the i2c register is stored in the low byte of i2c_reg + // shift it 8 bits to left for sending as coefficient data (16 bits) + // then or in the 8 byte data + // mask here or in cs_8409_vendor_coef_set? + i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | ( i2c_data & 0x0ff); + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg_data); + //if (retval == -1) + + + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + rdcnt = -8; +sleep2: + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + if (retval != -1) + { + retval &= 0x18; + if (retval != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep2; + } + } + } + + cs_8409_vendor_enableI2Cclock(codec, 0x0); + // exit on error + + myprintk_dbg("snd_hda_intel: i2cWrite 0x%04x 0x%04x: 0x%04x %d end",i2c_address,i2c_reg,i2c_data,paged); + + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + // exit on error + + return retval; +} + +static unsigned int cs_8409_vendor_i2cWriteMask(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int i2c_mask, unsigned int i2c_data, unsigned int paged) +{ + // masked version to emulate AppleHDATDMDevice::maskWriteReg(unsigned short, unsigned char, unsigned char) + + unsigned int retval; + unsigned int mask_val; + + retval = cs_8409_vendor_i2cRead(codec, i2c_address, i2c_reg, paged); + + mask_val = (retval & ~i2c_mask); + mask_val |= (i2c_data & i2c_mask); + + myprintk_dbg("snd_hda_intel: i2cWriteMask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",i2c_address,i2c_reg,mask_val,retval,i2c_data,i2c_mask,paged); + + retval = cs_8409_vendor_i2cWrite(codec, i2c_address, i2c_reg, mask_val, paged); + + return retval; +} + + +// this seems to be how to do a list of verbs +// there is command to do a sequence of these +// snd_hda_sequence_write +static const struct hda_verb cs8409_init_verbs[] = { + //{0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ + //{0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + {} /* terminator */ +}; + +struct hda_coef { + u16 write; + hda_nid_t nid; + u32 idx; + u32 param; + u32 retdata; + int srcidx; +}; + +// new feature to do a sequence of coef read/writes +// (seems to be used a lot for cs8409) +// note that we ignore the return for gets for the moment!! +// ooh - new idea - save the logged return and check +static const struct hda_coef cs8409_init_coef[] = { + //{0, 0x01, idx, 0x00, retdata, 0}, read + //{1, 0x01, idx, param, dmydata, 0}, write + //{2, 0x01, idx, param, retdata, 0}, write mask +}; + +void snd_hda_coef_item(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 retdata, int srcidx) +{ + if (write_flag == 2) + { + // NOTA BENE - just for initial debugging differentiation - pass a mask of 0xffff for total overwrite + // use snd_hda_coef_item_masked for actual masked setup + unsigned int retreadval = cs_8409_vendor_coef_set_mask(codec, idx, param, 0xffff, 0, srcidx); + if (retreadval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD mask return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retreadval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD mask return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retreadval,retdata,nid,idx,param); + } + } + else if (write_flag == 1) + cs_8409_vendor_coef_set(codec, idx, param); + else + { + unsigned int retval = cs_8409_vendor_coef_get(codec, idx); + if (retval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retval,retdata,nid,idx,param); + } + } +} + +// just create a special routine if we wish to return the actual value for the moment +int snd_hda_coef_item_check(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 retdata, int srcidx) +{ + int retval = 0; + + if (write_flag == 2) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_check %d\n", write_flag); + else if (write_flag == 1) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_check %d\n", write_flag); + else + { + unsigned int retval1 = cs_8409_vendor_coef_get(codec, idx); + if (retval1 != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retval1,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retval1,retdata,nid,idx,param); + } + retval = retval1; + } + + return retval; +} + +void snd_hda_coef_item_masked(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 mask, u32 retdata, u32 srcval, int srcidx) +{ + //int retval = 0; + if (write_flag != 2) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_masked %d\n", write_flag); + else + { + unsigned int retreadval = cs_8409_vendor_coef_set_mask(codec, idx, param, mask, srcval, srcidx); + if (retreadval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD mask return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retreadval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD mask return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retreadval,retdata,nid,idx,param); + } + } + //return retval; +} + +void snd_hda_coef_sequence(struct hda_codec *codec, const struct hda_coef *seq, char *prtstr) +{ + mycodec_info(codec, "start snd_hda_coef_sequence %s\n",prtstr); + for (; seq->nid; seq++) + { + snd_hda_coef_item(codec, seq->write, seq->nid, seq->idx, seq->param, seq->retdata, seq->srcidx); + } + mycodec_info(codec, "end snd_hda_coef_sequence %s\n",prtstr); +} + +static inline unsigned int snd_hda_codec_read_check(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm, unsigned int check_val, int srcidx) +{ + unsigned int retval; + retval = snd_hda_codec_read(codec, nid, flags, verb, parm); + + if (retval == -1) + return retval; + + if (srcidx > 0) + if (retval != check_val) + codec_dbg(codec, "command BAD read check return value at %d: 0x%08x 0x%08x (0x%02x, 0x%03x 0x%04x)\n",srcidx,retval,check_val,nid,verb,parm); + + return retval; +} + +void snd_hda_double_reset(struct hda_codec *codec) +{ + mycodec_info(codec, "snd_hda_double_reset\n"); + // still not clear if this does anything + snd_hda_codec_write(codec, codec->core.afg, 0, 0xfff, 0); + // so far the double reset seems to give bad results - lots of registers dont compare + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_CODEC_RESET, 0); + msleep(1); + // apparently should use usleep_range for a few ms + //usleep_range(1000,2000); + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_CODEC_RESET, 0); +} + + +static void clear_pins(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + hda_nid_t nid; + + mycodec_info(codec, "start clear_pins\n"); + + for_each_hda_codec_node(nid, codec) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + /* use read here for syncing after issuing each verb */ + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + mycodec_info(codec, "end clear_pins\n"); +} + + +static void read_coefs_all_loop(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + int idx; + mycodec_info(codec, "start read_coefs_all\n"); + for (idx = 0; idx < 130; idx++) + { + int retval = cs_8409_vendor_coef_get(codec, idx); + mycodec_info(codec,"snd_hda_intel: read_coefs_all 0x%02x: 0x%08x\n",idx,retval); + } + mycodec_info(codec, "end read_coefs_all\n"); +} + +// this is very hacky but until get more understanding of what we can do with the 8409 setup +// re-define these from hda_codec.c here +// NOTA BENE - need to check this is consistent with any hda_codec.c updates!! + +/* + * audio-converter setup caches + */ +struct hda_cvt_setup { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; + +// we now setup our local cache data in the spec structure +// - cvt_setups is an opaque pointer type so we can see it here +// but we dont know how to access the data - except by re-defining hda_cvt_setup as above + +/* get or create a cache entry for the given audio converter NID */ +static struct hda_cvt_setup * +get_hda_cvt_setup_8409(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_cvt_setup *p; + int i; + + snd_array_for_each(&codec->cvt_setups, i, p) { + if (p->nid == nid) + return p; + } + p = snd_array_new(&codec->cvt_setups); + if (p) + p->nid = nid; + return p; +} + + +// so we actually need both versions - one using the hda_cvt_setup struct +// and one using our local hda_cvt_setup_apple struct + +static struct hda_cvt_setup_apple * +get_hda_cvt_setup_apple_8409(struct hda_codec *codec, hda_nid_t nid) +{ + struct cs8409_apple_spec *spec = codec->spec; + + switch (nid) + { + case 0x02: + return &spec->nid_0x02; + case 0x03: + return &spec->nid_0x03; + case 0x0a: + return &spec->nid_0x0a; + case 0x22: + return &spec->nid_0x22; + case 0x23: + return &spec->nid_0x23; + case 0x1a: + return &spec->nid_0x1a; + default: + break; + } + + codec_err(codec, "get_hda_cvt_setup_apple_8409: UNKNOWN NID!! 0x%02x\n", nid); + + return NULL; +} + +static void cs_8409_dump_stream_format(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_cvt_setup_apple *p = NULL; + int i; + + // use explicit search so we dont create one if doesnt exist + + for (i = 0; i < codec->cvt_setups.used; i++) { + p = snd_array_elem(&codec->cvt_setups, i); + if (p->nid == nid) + break; + } + + if (p != NULL) + mycodec_dbg(codec, "cs_8409_dump_stream_format: NID=0x%x, codec cached values: stream=0x%x, channel=%d, format=0x%x\n", nid, p->stream_tag, p->channel_id, p->format_id); + else + mycodec_dbg(codec, "cs_8409_dump_stream_format: NID=0x%x, codec cached values: NULL\n", nid); +} + +static void cs_8409_reset_stream_format(struct hda_codec *codec, hda_nid_t nid, int format, int doreset) +{ + // note that this routine is currently not used + + // this resets the cached stream format so that next + // stream setup will actually rewrite the stream format and stream id + // or if doreset set it will perform the stream update now + // also allow for only updating the stream format and not stream id + + // NOTE we now save the stream format in our local cache as the hda_codec cache + // is cleared at end of the prepare stage and we want to store it more permanently + // really only the stream id is variable + + struct hda_cvt_setup *p = NULL; + struct hda_cvt_setup_apple *papl = NULL; + u32 stream_tag_sv; + int channel_id_sv; + int format_id_sv; + + // problem - the get_hda_cvt_setup function is local to hda_codec - so need our own copy above + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + stream_tag_sv = papl->stream_tag; + channel_id_sv = papl->channel_id; + format_id_sv = papl->format_id; + + mycodec_info(codec, "cs_8409_reset_stream_format RESET for nid 0x%02x: 0x%08x id 0x%08x chan 0x%08x\n", nid, format_id_sv, stream_tag_sv, channel_id_sv); + + // snd_hda_codec_setup_stream uses a caching system so only sends verbs when a change occurs + // we want to force a send here so need to clear the cached data + + p = get_hda_cvt_setup_8409(codec, nid); + + p->stream_tag = 0; + p->channel_id = 0; + if (format) + p->format_id = 0; + + if (doreset) + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, channel_id_sv, format_id_sv); + +} + +// so what do I want this to do +// the stream format will be stored in the hda_cvt_setup (at what stage is this valid??) +// - we want to remove the Apple specific stream format/channel setup +// and just call snd_hda_setup_stream - but we need the actual stream format for this +// - hopefully getting from the hda_cvt_setup struct +// unfortunately this idea of storing in the hda_cvt_setup table turns out to be not useful +// as at end of snd_hda_codec_prepare it clears out (ie zeros) all unused/inactive cache entries +// so we have to store in a separate cache using our own copied definition for hda_cvt_setup +// hda_cvt_setup_apple + + +// the following 2 functions are used in the sync converter functions +// where apple essentially disables streaming (set stream id to 0) updates some vendor nid parameters +// then restores streaming +// so we store the stream info in a local variable copy and set it to the unused stream id ie stream id of 0 +// then cs_8409_update_from_save_stream_format sets it back to what it was +// note that the format is unchanged for these operations +// the main reason for doing it this way is because of the caching used in snd_hda_codec_setup_stream +// - if we just sent the hda verbs then the cached data in snd_hda_codec_setup_stream +// would be inconsistent with the actual state of streaming on the nid + +static void cs_8409_save_and_clear_stream_format(struct hda_codec *codec, hda_nid_t nid, struct hda_cvt_setup *savep) +{ + struct hda_cvt_setup *p = NULL; + u32 stream_tag_sv; + int channel_id_sv; + int format_id_sv; + + mycodec_dbg(codec, "cs_8409_save_and_clear_stream_format nid 0x%02x\n", nid); + + // use this to save the stream format and clear the stream id and channel + + p = get_hda_cvt_setup_8409(codec, nid); + + savep->stream_tag = p->stream_tag; + savep->channel_id = p->channel_id; + savep->format_id = p->format_id; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); +} + +static void cs_8409_update_from_save_stream_format(struct hda_codec *codec, hda_nid_t nid, struct hda_cvt_setup *savep, int update_stream_id, int update_format_id) +{ + struct hda_cvt_setup *p = NULL; + + mycodec_dbg(codec, "cs_8409_update_from_save_stream_format nid 0x%02x\n", nid); + + // so this will ensure the format is re-updated + + p = get_hda_cvt_setup_8409(codec, nid); + + if (update_stream_id) + { + p->stream_tag = 0; + p->channel_id = 0; + } + if (update_format_id) + p->format_id = 0; + + mycodec_info(codec, "cs_8409_update_from_save_stream_format tag 0x%08x chnl 0x%08x fmt 0x%08x\n", savep->stream_tag, savep->channel_id, savep->format_id); + + snd_hda_codec_setup_stream(codec, nid, savep->stream_tag, savep->channel_id, savep->format_id); +} + + +// so these are the crucial routines for setting our local cached copy of the stream info (in the spec structure) +// we use a different struct definition (hda_cvt_setup_apple) to keep the re-definition of hda_cvt_setup more local +// note that this stream info is only stored once per stream prepare function call +// and this routine always updates from that initial data + +static void cs_8409_really_update_stream_format(struct hda_codec *codec, hda_nid_t nid, int update_format_id, int update_stream_id, unsigned int new_channel_id) +{ + struct hda_cvt_setup *p = NULL; + struct hda_cvt_setup_apple *papl = NULL; + u32 stream_tag_sv = 0; + int channel_id_sv = 0; + int format_id_sv = 0; + + mycodec_dbg(codec, "cs_8409_really_update_stream_format nid 0x%02x updfmt %d updstrmid %d nchnlid %d\n", nid, update_format_id, update_stream_id, new_channel_id); + //dump_stack(); + + // so here we take our local cached format and save locally, clear out the cached values + // then call snd_hda_codec_setup_stream with the cached values + // this will ensure we update the HDA with the stream format + + // maybe now we should just update from our local stored version?? + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + if (papl != NULL) + { + stream_tag_sv = papl->stream_tag; + channel_id_sv = papl->channel_id; + format_id_sv = papl->format_id; + } + else + { + codec_err(codec, "cs_8409_really_update_stream_format bad nid 0x%02x FAIL!!\n", nid); + return; + } + + + p = get_hda_cvt_setup_8409(codec, nid); + + mycodec_info(codec, "cs_8409_really_update_stream_format cached tag 0x%08x chnl 0x%08x fmt 0x%08x\n", papl->stream_tag, papl->channel_id, papl->format_id); + + if (update_stream_id) + { + p->stream_tag = 0; + p->channel_id = 0; + } + if (update_format_id) + p->format_id = 0; + + mycodec_info(codec, "cs_8409_really_update_stream_format to update tag 0x%08x chnl 0x%08x fmt 0x%08x\n", p->stream_tag, p->channel_id, p->format_id); + + if (update_stream_id == 2) + mycodec_info(codec, "cs_8409_really_update_stream_format new tag 0x%08x chnl 0x%08x fmt 0x%08x\n", stream_tag_sv, new_channel_id, format_id_sv); + else + mycodec_info(codec, "cs_8409_really_update_stream_format new tag 0x%08x chnl 0x%08x fmt 0x%08x\n", stream_tag_sv, channel_id_sv, format_id_sv); + + cs_8409_dump_stream_format(codec, nid); + + if (update_stream_id == 2) + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, new_channel_id, format_id_sv); + else + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, channel_id_sv, format_id_sv); +} + +// remove function from compile so get error when building if use it +#if 0 +static void cs_8409_setup_stream_format(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) +{ + struct hda_cvt_setup *p = NULL; + + // NOTE - this function should no longer be used + + mycodec_dbg(codec, "cs_8409_setup_stream_format nid 0x%02x\n",nid); + + cs_8409_dump_stream_format(codec, nid); + + // this functions sets up the cached stream - get_hda_cvt_setup_8409 creates a struct if not yet defined + // NOTA BENE we do not do the update here - we are relying that this will be done by a call to + // cs_8409_really_update_stream_format now we have set the format correctly + + p = get_hda_cvt_setup_8409(codec, nid); + + // NOTA BENE - we do not set the channel id here - this will be done by cs_8409_really_update_stream_format + + p->stream_tag = stream_tag; + p->channel_id = 0; + p->format_id = format; + + cs_8409_dump_stream_format(codec, nid); + + mycodec_dbg(codec, "end cs_8409_setup_stream_format\n"); +} +#endif + +static void cs_8409_store_stream_format(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) +{ + struct hda_cvt_setup_apple *papl = NULL; + + mycodec_dbg(codec, "cs_8409_store_stream_format nid 0x%02x\n",nid); + + cs_8409_dump_stream_format(codec, nid); + + // this functions sets up our local cached stream save store + // NOTA BENE we do not do the update here - we are relying that this will be done by a call to + // cs_8409_really_update_stream_format now we have set the format correctly + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + if (papl != NULL) + { + // NOTA BENE - we do not set the channel id here - this will be done by cs_8409_really_update_stream_format + + papl->stream_tag = stream_tag; + papl->channel_id = 0; + papl->format_id = format; + + mycodec_info(codec, "cs_8409_store_stream_format cached tag 0x%08x chnl 0x%08x fmt 0x%08x\n", papl->stream_tag, papl->channel_id, papl->format_id); + } + else + codec_err(codec, "cs_8409_store_stream_format bad nid 0x%02x FAIL!!\n", nid); + + mycodec_dbg(codec, "end cs_8409_store_stream_format\n"); +} + + +//int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); + +//static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) +// return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); + +// modified from init_input_src to switch the inputs on headset plugin/unplug events +static void switch_input_src(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + struct nid_path *path; + int i, c, nums; + mycodec_dbg(codec, "switch_input_src enter\n"); + + nums = spec->num_adc_nids; + + mycodec_dbg(codec, "switch_input_src num adc nids %d %d\n",nums,spec->dyn_adc_switch); + + for (c = 0; c < nums; c++) { + mycodec_dbg(codec, "switch_input_src num_items %d\n",imux->num_items); + for (i = 0; i < imux->num_items; i++) { + //path = get_input_path(codec, c, i); + path = snd_hda_get_path_from_idx(codec, spec->input_paths[i][c]); + if (path) { + int in; + bool active = path->active; + mycodec_dbg(codec, "switch_input_src path active %d\n",active); + for (in = path->depth - 1; in >= 0; in--) { + hda_nid_t tnid = path->path[in]; + mycodec_dbg(codec, "switch_input_src path nid %d: 0x%02x\n",in,tnid); + } + if (path->active) { + mycodec_dbg(codec, "switch_input_src path nid 0x%02x deactivate\n",path->path[1]); + snd_hda_activate_path(codec, path, false, false); + } else { + mycodec_dbg(codec, "switch_input_src path nid 0x%02x activate\n",path->path[1]); + snd_hda_activate_path(codec, path, true, false); + } + } + else { + mycodec_dbg(codec, "switch_input_src path NULL\n"); + } + } + } + + mycodec_dbg(codec, "switch_input_src exit\n"); +} + + + + +static int read_gpio_status_check(struct hda_codec *codec); + +#ifdef USE_DATA + +#include "patch_cirrus_data84.h" + +#include "patch_cirrus_plugin.h" + +#include "patch_cirrus_headplay.h" + +#include "patch_cirrus_unplug.h" + +#include "patch_cirrus_plugin3.h" + +#include "patch_cirrus_plugin23.h" + +#include "patch_cirrus_mb141_data84.h" + +#else + +// error definitions +static void cs_8409_external_device_unsolicited_response_data(struct hda_codec *codec, unsigned int res) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_boot_setup_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_play_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_play_real(struct hda_codec *codec); + +static void cs_8409_playstop_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_playstop_real(struct hda_codec *codec); + +static void cs_8409_headplay_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_headplay_real(struct hda_codec *codec); + +static void cs_8409_headplaystop_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_headplaystop_real(struct hda_codec *codec); + + +static void cs_8409_boot_setup_data_ssm3(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_play_data_ssm3(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + + +#endif + +#include "patch_cirrus_boot84.h" + +#include "patch_cirrus_real84_i2c.h" + +#include "patch_cirrus_real84.h" + +// only needed if wish to test the version using the mb141 logs +// cs_8409_boot_setup_real now supposed to do both machines +//#include "patch_cirrus_mb141_real84.h" + + +// macbook pro subsystem ids +// 14,1 0x106b3300 +// 14,2 0x106b3600 +// 14,3 0x106b3900 + +// imac subsystem ids +// 18,1 0x106b0e00 +// 18,2 0x106b0f00 +// 18,3 0x106b1000 +// 19,1 0x106b1000 + + +static int cs_8409_data_config(struct hda_codec *codec); +static int cs_8409_real_config(struct hda_codec *codec); + + +static int cs_8409_boot_setup(struct hda_codec *codec) +{ + int err = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // so it appears we break up the subsystem_id into 2 parts + // a codec vendor id (16 bits) and a subvendor id (8 bits) plus an assembly id + // so here the codec vendor is 0x106b, the subvendor id is 0x39 and the assembly id is 0x00 + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_data_config\n"); + + err = cs_8409_data_config(codec); + + myprintk("snd_hda_intel: cs_8409_boot_setup post cs_8409_data_config\n"); + } else { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_real_config\n"); + + err = cs_8409_real_config(codec); + + myprintk("snd_hda_intel: cs_8409_boot_setup post cs_8409_real_config\n"); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + cs_8409_boot_setup_data_ssm3(codec); + } else { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_real_config\n"); + //cs_8409_boot_setup_real_ssm3(codec); + err = cs_8409_real_config(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_boot_setup pre data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + err = cs_8409_real_config(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + err = -1; + } + + return err; +} + +void cs_8409_play_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + myprintk_dbg("snd_hda_intel: cs_8409_play_setup\n"); + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + //cs_8409_unmute_data(codec); + //cs_8409_volup_data(codec); + cs_8409_play_data(codec); + } else { + cs_8409_play_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_play_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_play_setup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_play_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +//static void cs_8409_playstop_data_ssm3(struct hda_codec *codec); +//static void cs_8409_playstop_real_ssm3(struct hda_codec *codec); + +void cs_8409_play_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + myprintk_dbg("snd_hda_intel: cs_8409_play_cleanup\n"); + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_playstop_data(codec); + } else { + cs_8409_playstop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_playstop_data_ssm3(codec); + } else { + //cs_8409_playstop_real_ssm3(codec); + cs_8409_playstop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_play_cleanup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_playstop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + +} + + +// NOTE - so far all systems use the same inputs for internal mike capturing - not sure if +// there are any subsystem_id differences + +void cs_8409_capture_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capture_data(codec); + } else { + //cs_8409_capture_real(codec); + cs_8409_capture_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + + +void cs_8409_capture_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capturestop_data(codec); + } else { + //cs_8409_capturestop_real(codec); + cs_8409_capturestop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + +} + + +static void cs_8409_cs42l83_unsolicited_response_finalize(struct hda_codec *codec, unsigned int res); + +static void cs_8409_perform_external_device_unsolicited_responses(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + struct unsol_item *unsol_entry = NULL; + struct unsol_item *unsol_temp = NULL; + if (!list_empty(&spec->unsol_list)) { + codec_info(codec, "cs_8409_perform_external_device_unsolicited_responses UNSOL start\n"); + list_for_each_entry_safe(unsol_entry, unsol_temp, &spec->unsol_list, list) + { + list_del_init(&unsol_entry->list); + // pigs this gets complicated - these might issue other unsol responses + cs_8409_cs42l83_unsolicited_response_finalize(codec, unsol_entry->res); + spec->unsol_items_prealloc_used[unsol_entry->idx] = 0; + memset(unsol_entry, 0, sizeof(struct unsol_item)); + } + codec_info(codec, "cs_8409_perform_external_device_unsolicited_responses UNSOL end\n"); + } +} + +static void cs_8409_cs42l83_unsolicited_response(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + // not clear if want to use the GPIO pins apparently passed in res to determine + // if want to do interrupt checking here and if no interrupts then to do + // some other unsolicited response function (not seen any such unsolicited responses yet) + // without checking the unsolicited block status + + // now think dont need a list - we can only have 1 outstanding unsolicted interrupt request + // we may get multiple unsolicited interrupt requests - but they all will have same GPIO status (0x26) + // and we determine the exact interrupt by reading the cs42l83 registers - which we are trying to avoid + // clashing with other verbs + // it may be that we get multiple interrupt flags to handle when we do read - not seen so far + + if (spec->block_unsol) + { + int itm; + int new_itm = -1; + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - UNSOL BLOCKED\n"); + for (itm=0; itm<10; itm++) + if (spec->unsol_items_prealloc_used[itm] == 0) { new_itm = itm; break; } + if (new_itm < 0) + { + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - IGNORING UNSOL RESPONSE!!\n"); + return; + } + spec->unsol_items_prealloc_used[new_itm] = 1; + memset(&(spec->unsol_items_prealloc[new_itm]), 0, sizeof(struct unsol_item)); + spec->unsol_items_prealloc[new_itm].res = res; + spec->unsol_items_prealloc[new_itm].idx = new_itm; + list_add_tail(&(spec->unsol_items_prealloc[new_itm].list), &spec->unsol_list); + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - UNSOL response stored\n"); + return; + } + else + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - NOT UNSOL BLOCKED\n"); + + // so it appears we need to block unsol responses while doing unsol responses + // this is probably not the way to do this but still havent figured out how to use locking properly + // as this needs to be interruptible because some of these functions take a long time + // I think if we get here we cannot have been blocked so list maybe always empty + // whats not clear is if list_for_each_entry_safe is safe for addition also + spec->block_unsol = 1; + + cs_8409_cs42l83_unsolicited_response_finalize(codec, res); + + if (!list_empty(&spec->unsol_list)) + { + mycodec_info(codec, "cs_8409_cs42l83_unsolicited_response - performing blocked responses start\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + mycodec_info(codec, "cs_8409_cs42l83_unsolicited_response - performing blocked responses end\n"); + } + + spec->block_unsol = 0; +} + +static void cs_8409_cs42l83_unsolicited_response_finalize(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (spec->use_data) + cs_8409_external_device_unsolicited_response_data(codec, res); + else + { + if (spec->headset_phase == 0) + { + mycodec_info(codec, "cs_8409_external_device_unsolicited_response_finalize - phase is 0 - skipping\n"); + return; + } + + + // note the data version will only play thro the headphones for a single time + //cs_8409_external_device_unsolicited_response_data(codec, res); + cs_8409_external_device_unsolicited_response(codec); + } +} + + +static void cs_8409_headset_mike_setup_nouse(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_intmike_linein_disable(codec); + + cs_8409_headset_mike_streaming_preplay(codec, 1); + cs_8409_headset_mike_buttons_enable(codec); +} + + +void cs_8409_headplay_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_headplay_data(codec); + } else { + cs_8409_headplay_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_headplay_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_headplay_setup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_headplay_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream setup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headplay_setup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +void cs_8409_headplay_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_headplaystop_data(codec); + } else { + //cs_8409_headplaystop_data(codec); + cs_8409_headplaystop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_headplaystop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_headplay_cleanup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_headplaystop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream cleanup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headplay_cleanup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +// NOTE - so far all systems use the same chip (cs42l83) for headset mike capturing - not sure if +// there are any subsystem_id differences + +void cs_8409_headcapture_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_headcapture_data(codec); + } else { + cs_8409_headcapture_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream setup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headcapture_setup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +void cs_8409_headcapture_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capturestop_data(codec); + } else { + //cs_8409_capturestop_real(codec); + cs_8409_headcapturestop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream cleanup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headcapturestop_cleanup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (action == HDA_GEN_PCM_ACT_PREPARE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 curtim; + ktime_get_real_ts64(&curtim); +#else + struct timespec curtim; + getnstimeofday(&curtim); +#endif + myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec); + if (1) { + struct hda_cvt_setup_apple *p = NULL; + //int power_chk = 0; + + // in the new way we set the stream up here using the passed data + // - this does not actually update the stream format here but sets the cached parameters + // so the cs_8409_really_update_stream_format will cause the updates to occur + // note we explicitly set the channel id - dont see another way yet + + //cs_8409_setup_stream_format(codec, 0x02, stream_tag, format); + cs_8409_store_stream_format(codec, 0x02, stream_tag, format); + + //cs_8409_setup_stream_format(codec, 0x03, stream_tag, format); + cs_8409_store_stream_format(codec, 0x03, stream_tag, format); + + //cs_8409_setup_stream_format(codec, 0x0a, stream_tag, format); + cs_8409_store_stream_format(codec, 0x0a, stream_tag, format); + + // save number of actual stream channels + spec->stream_channels = substream->runtime->channels; + + hda_check_power_state(codec, 0x1a, 1); + hda_check_power_state(codec, 0x3c, 1); + + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + // we need to split this to deal with capture only setup + // and capture with play setup + // note this does mean we setup the mike in a different order to OSX + // if we are capturing with playing - because the capture setup seems to be done + // first on Linux and we dont know at that stage if we will be playing + if (spec->have_mike) + { + // actually we always need to do cs_8409_headplay_setup - here we are about to play + // - what this possibly would allow is the apple way of doing a pre-setup + // so here we would switch between doing a full setup or a partial setup + if (spec->headset_play_format_setup_needed) + { + cs_8409_headplay_setup(codec); + spec->headset_play_format_setup_needed = 0; + } + // we only setup capturing if we are actually doing capturing + //if (spec->headset_capture_format_setup_needed) + //{ + // cs_8409_headcapture_setup(codec); + // spec->headset_capture_format_setup_needed = 0; + //} + } + else + { + cs_8409_headplay_setup(codec); + } + } + else { + cs_8409_play_setup(codec); + } + myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook setup play called"); + + hda_check_power_state(codec, 0x1a, 2); + hda_check_power_state(codec, 0x3c, 2); + + + // I dont now understand how this worked - the codes above ALWAYS reset the stream format + // to the OSX format + // and unless I force a stream update here there will be a stream format difference + // yet it appears it worked - even tho sometimes there was no format update after this routine + // now I dont know why + + + // so we need to force the stream to be re-set here + // problem is it appears hda_codec caches the stream format and id and only updates if changed + // and there doesnt seem to be a good way to force an update + + // this routine doesnt seem to be nid specific - so explicitly fix the known nids here + // no longer needed now we set the stream format correctly above + // so when snd_hda_multi_out_analog_prepare is called after this routine it should do nothing + // as we will have cached and set the right format now + + //cs_8409_reset_stream_format(codec, 0x02, 1, 0); + + //cs_8409_reset_stream_format(codec, 0x03, 1, 0); + + //cs_8409_reset_stream_format(codec, 0x0a, 1, 0); + + + spec->playing = 0; + + + spec->play_init = 1; + } + } +} + + +static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) +{ + + struct cs8409_apple_spec *spec = codec->spec; + + // so finally getting a handle on ordering here + // we need to do the OSX setup in the OPEN section + // as the generic hda format and stream setup is done BEFORE the PREPARE hook + // (theres a good chance we only need to do this once at least as long as machine doesnt sleep) + // (or we could just override the prepare function completely) + // I now think the noise was caused by mis-match between the stream format and the nid setup format + // (because the generic setup was done before the OSX setup and the actual streamed format is slightly different) + // (the hda documentation says these really need to match) + // It appears the 8409 setup can handle at least some differences in the stream format + // as long as we set the nid to format the kernel is sending + // certainly seems to handle S24_LE or S32_LE differences (OSX format is always S24_3LE) + + + if (action == HDA_GEN_PCM_ACT_OPEN) { + //struct hda_cvt_setup_apple *p = NULL; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open"); + + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open end"); + } else if (action == HDA_GEN_PCM_ACT_PREPARE) { + // so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 curtim; + ktime_get_real_ts64(&curtim); +#else + struct timespec curtim; + getnstimeofday(&curtim); +#endif + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec); + //int power_chk = 0; + //power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + //myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 2 %d", power_chk); + // this is where we need to finally unset the block_unsol + // - which also means this is where we should check for unsolicited responses + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + spec->playing = 1; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE end"); + } else if (action == HDA_GEN_PCM_ACT_CLEANUP) { + // so this also comes AFTER the stream format, frequency cleanup verbs are sent for the pcm stream + int power_chk = 0; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK CLEANUP"); + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 3 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook jack_present %d\n",spec->jack_present); + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + // so dont think need to anything about capturing here + cs_8409_headplay_cleanup(codec); + spec->headset_play_format_setup_needed = 1; + } + else + cs_8409_play_cleanup(codec); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook done play down"); + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + // not sure of this position yet + spec->playing = 0; + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 4 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK CLEANUP end"); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook close"); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook close end"); + } + +} + + +static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (action == HDA_GEN_PCM_ACT_PREPARE) { + myprintk("snd_hda_intel: command cs_8409_pcm_capture_pre_prepare_hook HOOK PREPARE init %d",spec->capture_init); + + // so the first action for internal mike recording (via Quicktime) + // is a headphone sense + // followed by amp setup for playing - is this just a feature of Quicktime?? + // maybe Quicktime just auto sets up play just in case + // we dont seem to have a headphone sense if we have already plugged in the headset + // not that we can do anything - except abort if no headset plugged in?? + + //if (hinfo->nid == 0x22) + //{ + // int retval; + // retval = cs42l83_headphone_sense(codec); + //} + + + // I think this is the same for intmike or headset mike + //cs_8409_setup_stream_format(codec, hinfo->nid, stream_tag, format); + cs_8409_store_stream_format(codec, hinfo->nid, stream_tag, format); + + + // for the moment have junky test here + if (spec->jack_present) { + spec->block_unsol = 1; + if (spec->have_mike) + { + // so it seems if we have a headset mike we always enable the + // headphones even if just capturing + if (spec->headset_play_format_setup_needed) + { + cs_8409_headplay_setup(codec); + spec->headset_play_format_setup_needed = 0; + } + if (spec->headset_capture_format_setup_needed) + { + cs_8409_headcapture_setup(codec); + spec->headset_capture_format_setup_needed = 0; + } + } + // I think this is impossible - this would say we tried to capture + // using a headset without mike + // NOTE - still not fixed linein/lineout working - this may need + // changing here + } + else + cs_8409_capture_setup(codec); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook setup capture called"); + + spec->capturing = 0; + + + spec->capture_init = 1; + } +} + +static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) +{ + + //struct cs8409_apple_spec *spec = codec->spec; + + struct cs8409_apple_spec *spec = NULL; + + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init called"); + + //dump_stack(); + + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init post stack"); + + // - so this seems to be the critical issue - this can apparently be called with a NULL codec!!! + // only thing to do seems to be to return!! + if (codec == NULL) { + struct hda_codec *badptr = NULL; + printk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NULL"); + // so if we are here it looks as tho we have been called from call_hp_automute + // - in which the codec is the 1st arg + badptr = (struct hda_codec *) hinfo; + spec = badptr->spec; + printk("snd_hda_intel: cs_8409_capture_pcm_hook - pcm_playback_hook %p", spec->gen.pcm_playback_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - pcm_capture_hook %p", spec->gen.pcm_capture_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - hp_automute_hook %p", spec->gen.hp_automute_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - line_automute_hook %p", spec->gen.line_automute_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - line_automute_hook %p", spec->gen.mic_autoswitch_hook); + printk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NULL exit"); + return; + } + else + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NOT NULL"); + + //dump_stack(); + + spec = codec->spec; + + + // so now no setup is done here - we only check for unsolicited responses + // - we do do cleanup for the CLEANUP action + + if (action == HDA_GEN_PCM_ACT_OPEN) { + //struct hda_cvt_setup_apple *p = NULL; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open"); + + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open end"); + } else if (action == HDA_GEN_PCM_ACT_PREPARE) { + // so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK PREPARE init %d",spec->capture_init); + // this is where we need to finally unset the block_unsol + // - which also means this is where we should check for unsolicited responses + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_capture_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + spec->capturing = 1; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK PREPARE end"); + } else if (action == HDA_GEN_PCM_ACT_CLEANUP) { + // so this also comes AFTER the stream format, frequency cleanup verbs are sent for the pcm stream + int power_chk = 0; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK CLEANUP"); + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook power check 0x01 3 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook jack_present %d\n",spec->jack_present); + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + if (spec->headset_capture_format_setup_needed == 0) + { + cs_8409_headcapture_cleanup(codec); + spec->headset_capture_format_setup_needed = 1; + } + if (!spec->playing) + { + if (spec->headset_play_format_setup_needed == 0) + { + cs_8409_headplay_cleanup(codec); + spec->headset_play_format_setup_needed = 1; + } + } + } + else + cs_8409_capture_cleanup(codec); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook done capture down"); + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_capture_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + // not sure of this position yet + spec->capturing = 0; + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook power check 0x01 4 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK CLEANUP end"); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook close"); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook close end"); + } + +} + + +// this version runs all explicit commands as logged on OSX +static int cs_8409_data_config(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + //hda_nid_t beep_nid = spec->beep_nid; + + unsigned int tmpstate1 = -1; + unsigned int tmpstate2 = -1; + unsigned int tmpstate3 = -1; + unsigned int tmpstate4 = -1; + + myprintk("snd_hda_intel: cs8409_data_config"); + + + cs_8409_boot_setup_data(codec); + + + // check what power state of these nodes is - Apple does not do this + tmpstate1 = hda_sync_power_state_8409(codec, 0x48, AC_PWRST_D0); + tmpstate2 = hda_sync_power_state_8409(codec, 0x49, AC_PWRST_D0); + tmpstate3 = hda_sync_power_state_8409(codec, 0x4a, AC_PWRST_D0); + tmpstate4 = hda_sync_power_state_8409(codec, 0x4b, AC_PWRST_D0); + + myprintk("snd_hda_intel: cs8409_data_config power 0x48 %d 0x49 %d 0x4a %d 0x4b %d\n",tmpstate1,tmpstate2,tmpstate3,tmpstate4); + + + myprintk("snd_hda_intel: cs8409_data_config end"); + + return 0; +} + + +// this version runs the setup using functions based on the setup using the logged data +static int cs_8409_real_config(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + //hda_nid_t beep_nid = spec->beep_nid; + + unsigned int tmpstate1 = -1; + unsigned int tmpstate2 = -1; + unsigned int tmpstate3 = -1; + unsigned int tmpstate4 = -1; + + myprintk("snd_hda_intel: cs8409_real_config"); + + + cs_8409_boot_setup_real(codec); + + + // check what power state of these nodes is - Apple does not do this + tmpstate1 = hda_sync_power_state_8409(codec, 0x48, AC_PWRST_D0); + tmpstate2 = hda_sync_power_state_8409(codec, 0x49, AC_PWRST_D0); + tmpstate3 = hda_sync_power_state_8409(codec, 0x4a, AC_PWRST_D0); + tmpstate4 = hda_sync_power_state_8409(codec, 0x4b, AC_PWRST_D0); + + myprintk("snd_hda_intel: cs8409_real_config power 0x48 %d 0x49 %d 0x4a %d 0x4b %d\n",tmpstate1,tmpstate2,tmpstate3,tmpstate4); + + + myprintk("snd_hda_intel: cs8409_real_config end"); + + return 0; +} + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h new file mode 100644 index 0000000..1a1cd04 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h @@ -0,0 +1,5954 @@ + +static int tdm_in_use(struct hda_codec *codec, int where_flag) +{ + int coef_ret = 0; + int coef_idx = 0; + + // re-implementation of AppleHDATDMBusManagerCS8409::tdmInUse + mycodec_info(codec, "command tdmInUse start %d\n", where_flag); + + // note on OSX the coef get functions returns a status value with read value stored in passed address + // on linux it seems -1 is an error return + + coef_ret = cs_8409_vendor_coef_get(codec, 0x19); + + //if (coef_ret == -1) error; + + coef_idx = 0x1a; + + do { + + if ((short)coef_ret >= 0) { + mycodec_info(codec, "command tdmInUse 1 end %d\n", where_flag); + return 1; + } + + coef_ret = cs_8409_vendor_coef_get(codec, coef_idx); + + //if (coef_ret == -1) error; + + coef_idx++; + + } while (coef_idx <= 0x57); + + mycodec_info(codec, "command tdmInUse 0 end %d\n", where_flag); + + return 0; + +} + + +static int cs42l83_headphone_sense(struct hda_codec *codec) +{ + int retval = 0; + + // AppleHDATDM_Codec::getHeadphonePinSense(bool*, unsigned int*) + + // register 0x1b77 - Detect Status 1 + // value 0x96 0x80 HP plugged bias 0x16 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7700 i2c data 0x7796 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b77, 1); // snd_hda + + mycodec_info(codec, "cs42l83_headphone_sense - 0x%04x\n", retval); + + return retval; +} + +static int read_gpio_status_check(struct hda_codec *codec) +{ + int retval; + + // should these be done powered down?? + // lets check power state here + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command read_gpio_status_check %x\n", retval); + + return retval; +} + + +static void cs_8409_intmike_format_setup_format33_nouse(struct hda_codec *codec) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 +// snd_hda: stream format 34 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + +} + +static void cs_8409_intmike_format_setup_format_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x02224031 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x02224031 +// snd_hda: stream format 34 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + +} + +static void cs_8409_intmike_format_setup_enable(struct hda_codec *codec, int hda_format, int powered_down) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + // now updated to not write the Apple format but use my format setting routines + // (remember we have limited the allowed formats to acceptable ones) + // note that apparently we can set the format with the nid powered down but for setting the + // stream id the nid has to be powered up + // this seems to be used a lot in plugin/unplug headset in a powered down state + // - but when capturing no power changes done + + // for some very strange reason we setup a 4 channel format after unplug of headset with mike + // - otherwise its 2 channel - pass the format to allow for this + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 +// snd_hda: stream format 34 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, hda_format); // 0x02224033 + + + // now assuming have saved the stream info prior to calling this function + + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10515); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10518); // 0x022f0500 + //if (powered_down) hda_set_node_power_state(codec, 0x22, AC_PWRST_D0); + if (powered_down) hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 1)] + + + // using the stored stream parameters update nid 0x22 stream parameters + // we have limited the allowed formats so should only have working formats here + //cs_8409_really_update_stream_format(codec, 0x22, 1, 1, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 1, 0); + + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10521); // 0x022f0500 + //if (powered_down) hda_set_node_power_state(codec, 0x22, AC_PWRST_D3); + if (powered_down) hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + +// snd_hda: # AppleHDAWidgetCS8409::setConnectionSelect: + ret_coef9 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x000000b3, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef read 10523 + //new_coef9 = (ret_coef9 | 0x20); // note most of the time it just seems to copy the value because bit 0x20 already set on input + // // only on boot does this get set + new_coef9 = (ret_coef9 | spec->reg9_intmike_dmic_mo); // note most of the time it just seems to copy the value because bit 0x20 already set on input + // only on boot does this get set + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_format_setup_enable coef 0x09 update 0x%04x 0x%04x \n", ret_coef9, new_coef9); + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0x00000000, 10527 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10527 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, new_coef9, 0x00000000, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10527 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02270100 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02270100 + +} + + +static void cs_8409_volume_set(struct hda_codec *codec, hda_nid_t nid, int volume) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236027 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_volume_mute(struct hda_codec *codec, hda_nid_t nid) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236000 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_volume_unmute(struct hda_codec *codec, hda_nid_t nid) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236000 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_intmike_volume_set(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_set(codec, spec->intmike_adc_nid, volume); +} + +static void cs_8409_linein_volume_set(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_set(codec, spec->linein_amp_nid, volume); +} + +static void cs_8409_intmike_volume_unmute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_unmute(codec, spec->intmike_adc_nid); +} + +static void cs_8409_linein_volume_unmute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_unmute(codec, spec->linein_amp_nid); +} + +static void cs_8409_intmike_volume_mute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_mute(codec, spec->intmike_adc_nid); +} + +static void cs_8409_linein_volume_mute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_mute(codec, spec->linein_amp_nid); +} + +static void cs_8409_intmike_volume_setup_new(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_volume_set(codec, spec->intmike_adc_nid, volume); + + cs_8409_volume_mute(codec, spec->intmike_adc_nid); + + cs_8409_volume_set(codec, spec->intmike_nid, 0x00); + +} + +static void cs_8409_linein_volume_setup_new(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_volume_set(codec, spec->linein_amp_nid, volume); + + cs_8409_volume_mute(codec, spec->linein_amp_nid); + + cs_8409_volume_set(codec, spec->linein_nid, 0x00); + +} + + +static void cs_8409_intmike_volume_setup(struct hda_codec *codec, int volume) +{ + int retgain; + int newgain; + + struct cs8409_apple_spec *spec = codec->spec; + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000033, 0); // 0x022b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000033, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x0033 mute 0 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236027 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006027); // 0x02236027 +// snd_hda: amp gain/mute 34 0x6027 mute 0 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000033, 0); // 0x022b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000033, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x0033 mute 0 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005027); // 0x02235027 +// snd_hda: amp gain/mute 34 0x5027 mute 0 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + + // mute + // plausibly AppleHDAWidget::setWidgetAmplifierMute + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000027, 0); // 0x022b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000027, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x0027 mute 0 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x022360a7 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x022360a7 +// snd_hda: amp gain/mute 34 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000027, 0); // 0x022b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000027, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x0027 mute 0 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x022350a7 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x022350a7 +// snd_hda: amp gain/mute 34 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + + // this is working on node 0x44 macbook pro + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x044b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x044b2000 +// snd_hda: amp gain/mute 68 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 68 0x0000 mute 0 gain 0x0 0 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x04436000 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04436000 +// snd_hda: amp gain/mute 68 0x6000 mute 0 gain 0x0 0 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x044b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x044b0000 +// snd_hda: amp gain/mute 68 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 68 0x0000 mute 0 gain 0x0 0 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x04435000 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04435000 +// snd_hda: amp gain/mute 68 0x5000 mute 0 gain 0x0 0 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_setup(struct hda_codec *codec, int volume) +{ + int retgain; + int newgain; + + struct cs8409_apple_spec *spec = codec->spec; + + // so as far as I can see the 1st section sets the gain and the second section sets the mute + // it appears we do masked updates + + //volume = 0x27; + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 +// snd_hda: amp gain/mute 35 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023350a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x023350a7 +// snd_hda: amp gain/mute 35 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + // mute + // plausibly AppleHDAWidget::setWidgetAmplifierMute + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023360a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 +// snd_hda: amp gain/mute 35 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023350a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x023350a7 +// snd_hda: amp gain/mute 35 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + // this is working on node 0x45 macbook pro + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x045b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x045b2000 +// snd_hda: amp gain/mute 69 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 69 0x0000 mute 0 gain 0x0 0 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04536000 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04536000 +// snd_hda: amp gain/mute 69 0x6000 mute 0 gain 0x0 0 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x045b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x045b0000 +// snd_hda: amp gain/mute 69 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 69 0x0000 mute 0 gain 0x0 0 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04535000 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04535000 +// snd_hda: amp gain/mute 69 0x5000 mute 0 gain 0x0 0 index 0x00 left 0 right 1 output 0 input 1 right input + +} + + +static void cs_8409_intmike_stream_on_nid(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00005400, 0 ); // coef read 10544 + + //new_coef82 = (reg_coef82 | 0x1); + new_coef82 = (reg_coef82 | spec->reg82_intmike_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_stream_on_nid coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x5401, 0x00000000, 10548 ); // coef write 10548 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 10548 + + //retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x044f0700 + retval = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x044f0700 + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04470720 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04470720 +// snd_hda: 68 ['AC_PINCTL_IN_EN'] + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void cs_8409_intmike_format_setup_disable(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + + // set to defaults and disable input + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12217); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12220); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12223); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02220000 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02220000 +// snd_hda: stream format 34 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + // AppleHDAWidgetCS8409::configurePinForIO(bool)?? + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000a801, 0 ); // coef read 12226 + + //new_coef82 = (reg_coef82 & 0xfffffffe); + new_coef82 = (reg_coef82 & ~spec->reg82_intmike_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_format_setup_disable coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x00000000, 12230 ); // coef write 12230 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 12230 + + //retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x044f0700 + retval = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x044f0700 + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 +// snd_hda: 68 [] + +} + + +static void cs_8409_linein_format_setup_disable(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x45 -> 0x23 is line input (macbook pro) + + + // set to defaults and disable input + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12248); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12251); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12254); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02320000 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02320000 +// snd_hda: stream format 35 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000a800, 0 ); // coef read 12257 + + //new_coef82 = (reg_coef82 & 0xfffffffd); + new_coef82 = (reg_coef82 & ~spec->reg82_linein_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_format_setup_disable coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x00000000, 12261 ); // coef write 12261 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 12261 + + //retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + retval = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 +// snd_hda: 69 [] + +} + + +static void cs_8409_intmike_stream_conn_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12160); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12163); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12166); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this seems to be updating the coef index associated with setConnectionSelect + // unable to figure where this is coming from currently +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0033, 0xffff, 0x00000033, 0, 12168 ); // coef write mask 12168 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0000, 0x00000033, 0x0033, 0 ); // coef write mask 12168 + +} + +static void cs_8409_linein_stream_conn_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // Im thinking of a bugfix here to turn off bit 0x80 of index 0x0009 + + // 0x45 -> 0x23 is line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x023f0600 + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x023f0600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12175); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12178); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12181); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + // this seems to be updating the coef index associated with setConnectionSelect + // unable to figure where this is coming from currently +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0033, 0xffff, 0x00000033, 0, 12183 ); // coef write mask 12183 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0000, 0x00000033, 0x0033, 0 ); // coef write mask 12183 + // possible correct mask to use + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0080, 0x00000033, 0, 0 ); // coef write mask 12183 + +} + +static void cs_8409_intmike_stream_off_nid(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12189); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12192); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12195); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + +} + +static void cs_8409_linein_stream_off_nid(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12197); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12200); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12203); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + +} + + +static void cs_8409_intmike_volume_mute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x44 is connected to 0x22 which is labelled mic input (macbook pro) + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x022b2000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x022360b3 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x022360b3 +// snd_hda: amp gain/mute 34 0x60b3 mute 1 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x022b0000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x022350b3 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x022350b3 +// snd_hda: amp gain/mute 34 0x50b3 mute 1 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_mute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x45 which are connected to 0x23 is labelled as line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x023360b3 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x023360b3 +// snd_hda: amp gain/mute 35 0x60b3 mute 1 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x023350b3 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x023350b3 +// snd_hda: amp gain/mute 35 0x50b3 mute 1 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_intmike_volume_unmute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x44 is connected to 0x22 which is labelled mic input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x022b2000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02236033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02236033 +// snd_hda: amp gain/mute 34 0x6033 mute 0 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x022b0000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02235033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02235033 +// snd_hda: amp gain/mute 34 0x5033 mute 0 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_unmute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x45 which are connected to 0x23 is labelled as line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02336033 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02336033 +// snd_hda: amp gain/mute 35 0x6033 mute 0 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02335033 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02335033 +// snd_hda: amp gain/mute 35 0x5033 mute 0 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + + +static void cs_8409_setup_TDM_sample_rate(struct hda_codec *codec) +{ + + // codes from windows cs4208_38.inf file + // 0x0001 undocumented (0x0066 = ASP1/2_EN = 1, ASP1/2_STP = 1 ie ASP1_EN = 0x40, ASP2_EN = 0x20, ASP1_STP = 0x4, ASP2_STP = 0x2) + // 0x0005 0x0001 SCDIV 1:4 (0x005a = ASP1: MCEN = 0, FSD = 010 (0x40), SCPOL_IN/OUT = 1 (0x10), SCDIV = 1:4 ie 0x0-0xf) + // 0x0004 0x08ff SC_SRCSEL? = PLL1, LCPR = 0xff (0x28FF = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = FFh), 0x2801 = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = 01h)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setSampleRate: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0xffff, 0x00000200, 0, 25 ); // coef write mask 25 + + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0xffff, 0x00000001, 0, 31 ); // coef write mask 31 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0xffff, 0x000008ff, 0, 37 ); // coef write mask 37 + + // we need to use proper masked versions here - in particular for register 1 which seems to be some form of enable control + // for the subsystems and bits 0x7f need to pass thro here + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x0380, 0x00000200, 0, 0 ); // coef write mask 25 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0x0007, 0x00000001, 0, 0 ); // coef write mask 31 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0x01ff, 0x000008ff, 0, 0 ); // coef write mask 37 +} + +static void cs_8409_setup_TDM_proper_amps12(struct hda_codec *codec) +{ + int ret_coef0 = 0; + int new_coef0 = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + // codes from windows cs4208_38.inf file + // 0x0019 0x0800 = (ASP1.A: TX.LAP = 0, TX.LSZ = 24 bits, TX.LCS = 0) + // 0x001a 0x0820 = (ASP1.A: TX.RAP = 0, TX.RSZ = 24 bits, TX.RCS = 32) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 44 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0019, 0x0800, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 48 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 52 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001a, 0x0820, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 56 + + // codes from windows cs4208_38.inf file + // 0x0000 0xb000 = PLL2_EN (0x2000) PLL1_EN (0x1000) I2C disabled + // 0x0004 0x08ff SC_SRCSEL? = PLL1, LCPR = 0xff (0x28FF = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = FFh), 0x2801 = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = 01h)) + // 0x0000 0x9000 = PLL1_EN (0x1000) I2C disabled + + ret_coef0 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 60 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 64 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0004, 0x0000, 0x000008ff, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 68 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 72 + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9000, 0x00000000, 76 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 76 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + // it seems we re-write what we read just above here - if never plugin headphones its 0x9000 + // after headphone plugin its 0xb0000 - even after unplugging headphones!! + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, ret_coef0, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + + // codes from windows cs4208_38.inf file + // 0x0003 0x8000 = (ASP1: LCHI = 00h) + // 0x0005 0x0001 = SCDIV = 1:4 (0x005A = (ASP1: MCEN = 0, FSD = 010 (0x40), SCPOL_IN/OUT = 1 (0x10), SCDIV = 1:4)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0003, 0x8000, 0xffff, 0x00008000, 0, 0 ); // coef write mask 80 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 86 + + // codes from windows cs4208_38.inf file + // 0x0082 0x5401 = ASP1_xxx_EN (0x4000) ASP1_xxx_EN (0x1000) ASP1_xxx_EN (0x400) DMIC1_SCL_EN = 1 (0xFC01 = (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5401, 0xffff, 0x00000001, 0, 92 ); // coef write mask 92 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5400, 0x0000, 0x00000001, 0x5401, 0 ); // coef write mask 92 + + // codes from windows cs4208_38.inf file + // 0x0002 0x0280 = ASP2_BUS_IDLE (0x02) GPIO_I2C (0x0A80 = (ASP1/2_BUS_IDLE = 10, +GPIO_I2C)) + + // no evidence this is anything other than 0x0280 yet + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0002, 0x0280, 0xffff, 0x00000280, 0, 0 ); // coef write mask 98 + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 104 + + new_coef1 = (ret_coef1 & 0xffff) | 0x20; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps12 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0220, 0x00000000, 108 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 108 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 108 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 112 ); // coef read 112 + + tdm_in_use(codec, 1); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 117 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 121 + + new_coef71 = (ret_coef71 & 0xffff) | 0x400f; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps12 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 125 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 125 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 125 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 128 +} + +static void cs_8409_setup_TDM_amps12(struct hda_codec *codec, int setrate, int nullformat) +{ + int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + + // this seems to be setup for node 0x02 chain - which seems to use node 0x24 and amps 0x64 and 0x62 (or 0x28 0x2a) + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 +// snd_hda: stream format 2 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00270610 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4033 is Apples fixed format - but this is for boot stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 + } + else + { + // using the stored stream parameters update nid 0x2 stream parameters + // we have limited the allowed formats so should only have working formats here + cs_8409_really_update_stream_format(codec, 0x02, 1, 2, 0); + } + + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 16 + + new_coef1 = (ret_coef1 & 0xffff); // not clear what this is setting - no difference between read and write + // however if used in different places the actual value may be different + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_amps12 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 20 ); // coef write 20 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 20 + + if (setrate) + cs_8409_setup_TDM_sample_rate(codec); + + + cs_8409_setup_TDM_proper_amps12(codec); + + + // enable output node 0x24 + + //retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 132); // 0x024f0700 + retval = snd_hda_codec_read(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000040); // 0x02470740 +// snd_hda: 36 ['AC_PINCTL_OUT_EN'] + +} + + +static void play_setup_TDM_amps12(struct hda_codec *codec, int setrate) +{ + cs_8409_setup_TDM_amps12(codec, setrate, 0); +} + + +static void cs_8409_setup_amp_max(struct hda_codec *codec, int amp_address, int amp_volume, int channel); + +static void cs_8409_setup_amp_ssm3(struct hda_codec *codec, int amp_address, int amp_volume, int channel); + +static void cs_8409_setup_amp_tas576(struct hda_codec *codec, int amp_address, int amp_volume, int channel, int amp_enable); + +static void cs_8409_setup_amp_tas576_amp0(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp1(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp2(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp3(struct hda_codec *codec, int amp_volume, int amp_enable); + +static void cs_8409_setup_amps12(struct hda_codec *codec, int amps_enable) +{ + if (codec->core.subsystem_id == 0x106b3900) { + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_max(codec, 0x64, 0x01, 0x00); + cs_8409_setup_amp_max(codec, 0x62, 0x01, 0x02); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + //cs_8409_setup_amp_max(codec, 0x64, 0x30, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x30, 0x02); + // original mapping: + //cs_8409_setup_amp_max(codec, 0x64, 0x30, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x30, 0x01); + //cs_8409_setup_amp_max(codec, 0x64, 0x01, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x01, 0x01); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_ssm3(codec, 0x28, 0x48, 0x00); + cs_8409_setup_amp_ssm3(codec, 0x2a, 0x48, 0x02); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x80, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x80, 0x02); + // original mapping: + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x80, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x80, 0x01); + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x48, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x48, 0x01); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // NOTE that the TAS57642 amps setup during boot seems to skip the actual speaker power on + // hence the addition of the amps_enable parameter + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_tas576(codec, 0xd8, 0xcf, 0x00, amps_enable); + cs_8409_setup_amp_tas576(codec, 0xda, 0xcf, 0x02, amps_enable); + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + //cs_8409_setup_amp_tas576(codec, 0xd8, 0x9f, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0x9f, 0x02, amps_enable); + // original mapping: + //cs_8409_setup_amp_tas576(codec, 0xd8, 0x9f, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0x9f, 0x01, amps_enable); + //cs_8409_setup_amp_tas576_amp0(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576_amp1(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xd8, 0xcf, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0xcf, 0x01, amps_enable); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void play_setup_amps12(struct hda_codec *codec) +{ + cs_8409_setup_amps12(codec, 1); +} + + +static void cs_8409_setup_TDM_proper_amps34(struct hda_codec *codec) +{ + int ret_coef71 = 0; + int new_coef71 = 0; + + // codes from windows cs4208_38.inf file + // 0x001b 0x0840 = (ASP1.B: TX.LAP = 0, TX.LSZ = 24 bits, TX.LCS = 64) + // 0x001c 0x0860 = (ASP1.B: TX.RAP = 0, TX.RSZ = 24 bits, TX.RCS = 96) + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00008840, 0 ); // coef read 1407 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001b, 0x0840, 0x00000000, 0 ); // coef write 1411 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00008860, 0 ); // coef read 1415 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001c, 0x0860, 0x00000000, 0 ); // coef write 1419 + + // the following actually reads from 0x19 to 0x57 in a loop if the snd_hda_coef_item returns 0 till the read value + // does not have the word sign bit set (ie 0x8000) or finish all 0x57 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 1423 ); // coef read 1423 + + tdm_in_use(codec, 2); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1428 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000400f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 1432 + + new_coef71 = (ret_coef71 & 0xffff) | 0x400f; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps34 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 1436 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1436 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1436 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 1439 + +} + +static void cs_8409_setup_TDM_amps34(struct hda_codec *codec, int nullformat) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // this seems to be setup for node 0x03 chain - which seems to use node 0x25 and amps 0x74 and 0x72 (or 0x2c and 0x2e) + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00324033 +// snd_hda: stream format 3 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x00370612 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4033 is Apples fixed format - but this is for boot stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + // note this updates the channel but not stream id???? + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000002); // 0x00270602 + } + else + { + // using the stored stream parameters update nid 0x3 stream parameters + // we have limited the allowed formats so should only have working formats here + // now realised if we want to pass 2 channel streams to driver then we need to apply same + // fixup as in snd_hda_multi_out_analog_prepare ie set the channel id of the second nid + // to same as first nid for 2 channel inputs - rather than 2 if have a 4 channel stream + mydev_info(hda_codec_dev(codec), "cs_8409_setup_TDM_amps34: stream channels %d\n", spec->stream_channels); + if (spec->stream_channels == 2) + cs_8409_really_update_stream_format(codec, 0x03, 1, 2, 0); + else + cs_8409_really_update_stream_format(codec, 0x03, 1, 2, 2); + } + + cs_8409_setup_TDM_proper_amps34(codec); + + + // enable output on node 0x25 + + //retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 1443); // 0x025f0700 + retval = snd_hda_codec_read(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000040); // 0x02570740 +// snd_hda: 37 ['AC_PINCTL_OUT_EN'] + +} + + +static void play_setup_TDM_amps34(struct hda_codec *codec) +{ + cs_8409_setup_TDM_amps34(codec, 0); +} + +static void cs_8409_setup_amps34(struct hda_codec *codec, int amps_enable) +{ + if (codec->core.subsystem_id == 0x106b3900) { + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_max(codec, 0x74, 0x01, 0x01); + cs_8409_setup_amp_max(codec, 0x72, 0x01, 0x03); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + //cs_8409_setup_amp_max(codec, 0x74, 0x30, 0x01); + //cs_8409_setup_amp_max(codec, 0x72, 0x30, 0x03); + // original mapping: + //cs_8409_setup_amp_max(codec, 0x74, 0x30, 0x02); + //cs_8409_setup_amp_max(codec, 0x72, 0x30, 0x03); + //cs_8409_setup_amp_max(codec, 0x74, 0x01, 0x02); + //cs_8409_setup_amp_max(codec, 0x72, 0x01, 0x03); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_ssm3(codec, 0x2c, 0x48, 0x01); + cs_8409_setup_amp_ssm3(codec, 0x2e, 0x48, 0x03); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x80, 0x01); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x80, 0x03); + // original mapping: + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x80, 0x02); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x80, 0x03); + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x48, 0x02); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x48, 0x03); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // NOTE that the TAS57642 amps setup during boot seems to skip the actual speaker power on + // hence the addition of the amps_enable parameter + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_tas576(codec, 0xdc, 0xcf, 0x01, amps_enable); + cs_8409_setup_amp_tas576(codec, 0xde, 0xcf, 0x03, amps_enable); + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + //cs_8409_setup_amp_tas576(codec, 0xdc, 0x9f, 0x01, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0x9f, 0x03, amps_enable); + // original mapping: + //cs_8409_setup_amp_tas576(codec, 0xdc, 0x9f, 0x02, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0x9f, 0x03, amps_enable); + //cs_8409_setup_amp_tas576_amp2(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576_amp3(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xdc, 0xcf, 0x02, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0xcf, 0x03, amps_enable); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void play_setup_amps34(struct hda_codec *codec) +{ + cs_8409_setup_amps34(codec, 1); +} + +static void cs_8409_sync_converters_on(struct hda_codec *codec, int nullformat) +{ + int retval; + + struct hda_cvt_setup p2; + struct hda_cvt_setup p3; + + // this stops streaming on nodes 0x2 and 0x3 by switching to stream index 0 + // then updates vendor node coef index 0x0017 twice + // first with 0x1 for node 0x2 and then with 0x2 for node 0x3 + // giving a final 0x3 + // then sets up ready for streaming again + + // so coef index 0x17 is likely turning on the TDM stream + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // remove normal channel mapping + +// snd_hda: # AppleHDAFunctionGroupCS8409::syncConverters: + //retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000010, 2714); // 0x002f0600 + //retval = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONV, 0x00000000); // 0x002f0600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 0)] + + cs_8409_save_and_clear_stream_format(codec, 0x02, &p2); + + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0017, 0x0001, 0xffff, 0x00000000, 0, 0 ); // coef write mask 2716 + + +// snd_hda: # AppleHDAFunctionGroupCS8409::syncConverters: + //retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000012, 2722); // 0x003f0600 + //retval = snd_hda_codec_read(codec, 0x03, 0, AC_VERB_GET_CONV, 0x00000000); // 0x003f0600 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00370600 +// snd_hda: conv stream channel map 3 [('CHAN', 0), ('STREAMID', 0)] + + cs_8409_save_and_clear_stream_format(codec, 0x03, &p3); + + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0017, 0x0003, 0xffff, 0x00000001, 0, 0 ); // coef write mask 2724 + + + // and reset back to normal channel mapping + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00270610 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x00370612 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + if (nullformat) + { + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000002); // 0x00270602 + } + else + { + cs_8409_update_from_save_stream_format(codec, 0x02, &p2, 1, 0); + cs_8409_update_from_save_stream_format(codec, 0x03, &p3, 1, 0); + } +} + + +static void play_sync_converters_on(struct hda_codec *codec) +{ + cs_8409_sync_converters_on(codec, 0); +} + + + +static void cs_8409_setup_amp_max(struct hda_codec *codec, int amp_address, int amp_volume, int channel) +{ + //int retval; + + // NOTA BENE - reduced volume from 0x01 to 0x30 - seems to fit with linux levels much better + // this is semi arbitrary - just hears about OK - not based on rational analysis of amp gains + + + // HPFDCBlocker new as of June 2019 + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1c01 i2c data 0x0001 reg anal: DigitalFilter : HPFDCBlocker +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1008 i2c data 0x0008 reg anal: PCMClockSetup : 256 Bclks +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x14e4 i2c data 0x00e4 reg anal: PCMModeConfig : 32 bits TDM mode 2 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1501 i2c data 0x0001 reg anal: PCMRXEnablesA +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1600 i2c data 0x0000 reg anal: PCMRXEnablesB +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1800 i2c data 0x0000 reg anal: MonoMixChannelSource +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x2d01 i2c data 0x0001 reg anal: DigitalVolCtrl +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x2e05 i2c data 0x0005 reg anal: PathGain +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x4a21 i2c data 0x0021 reg anal: SpeakerEnable : AmpEnabled +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x4d07 i2c data 0x0007 reg anal: RestartBehavior +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5534 i2c data 0x0034 reg anal: LimiterAttackRelease +// snd_hda i2cRead i2c address 0x64 i2c reg 0x1100 i2c data 0x1107 reg anal: PCMSampleSetup : 44.1kHz +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1107 i2c data 0x0007 reg anal: PCMSampleSetup : 44.1kHz +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x093f i2c data 0x003f reg anal: InterruptClears0 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x0a7f i2c data 0x007f reg anal: InterruptClears1 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x0f0e i2c data 0x000e reg anal: IRQClear1 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5001 i2c data 0x0001 reg anal: GlobalEnable : Enable + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x001c, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0010, 0x0008, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0014, 0x00e4, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0015, (1<core.subsystem_id == 0x106b3900) { + cs_8409_disable_amp_max(codec, 0x64); + cs_8409_disable_amp_max(codec, 0x62); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + cs_8409_disable_amp_ssm3(codec, 0x28); + cs_8409_disable_amp_ssm3(codec, 0x2a); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //cs_8409_disable_amp_tas576(codec, 0xd8); + //cs_8409_disable_amp_tas576(codec, 0xda); + cs_8409_disable_amp_tas576_amp0(codec); + cs_8409_disable_amp_tas576_amp1(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void playstop_disable_amps12(struct hda_codec *codec) +{ + cs_8409_disable_amps12(codec); +} + +static void cs_8409_disable_TDM_proper_amps12(struct hda_codec *codec) +{ + //int retval; + int ret_coef71 = 0; + int new_coef71 = 0; + + // this seems to be setup for node 0x02 chain - which seems to use node 0x24 and amps 0x64 and 0x62 (or 0x28 and 0x2a) + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 0 ); // coef read 341 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0019, 0x8800, 0x00000000, 0 ); // coef write 345 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00000820, 0 ); // coef read 349 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001a, 0x8820, 0x00000000, 0 ); // coef write 353 + + // snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 357 ); // coef read 357 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 361 ); // coef read 361 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00000840, 365 ); // coef read 365 + + tdm_in_use(codec, 30); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 370 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000400f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 374 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs_8409_disable_TDM_proper_amps12 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 378 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 378 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 378 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 381 +} + + +static void cs_8409_disable_TDM_amps12(struct hda_codec *codec) +{ + int retval; + + cs_8409_disable_TDM_proper_amps12(codec); + + + // set to defaults and disable output + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00220000 +// snd_hda: stream format 2 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 387); // 0x024f0700 + retval = snd_hda_codec_read(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02470700 +// snd_hda: 36 [] + +} + +static void playstop_disable_TDM_amps12(struct hda_codec *codec) +{ + cs_8409_disable_TDM_amps12(codec); +} + +static void cs_8409_disable_amps34(struct hda_codec *codec) +{ + if (codec->core.subsystem_id == 0x106b3900) { + cs_8409_disable_amp_max(codec, 0x74); + cs_8409_disable_amp_max(codec, 0x72); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + cs_8409_disable_amp_ssm3(codec, 0x2c); + cs_8409_disable_amp_ssm3(codec, 0x2e); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //cs_8409_disable_amp_tas576(codec, 0xdc); + //cs_8409_disable_amp_tas576(codec, 0xde); + cs_8409_disable_amp_tas576_amp2(codec); + cs_8409_disable_amp_tas576_amp3(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void playstop_disable_amps34(struct hda_codec *codec) +{ + cs_8409_disable_amps34(codec); +} + + +static void cs_8409_disable_TDM_proper_amps34(struct hda_codec *codec) +{ + //int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + + // AppleHDATDMBusManagerCS8409::disableTDMPath + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00000840, 0 ); // coef read 694 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001b, 0x8840, 0x00000000, 0 ); // coef write 698 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00000860, 0 ); // coef read 702 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001c, 0x8860, 0x00000000, 0 ); // coef write 706 + + // AppleHDATDMBusManagerCS8409::disableTDMPath +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0001, 0xffff, 0x00005401, 0, 710 ); // coef write mask 710 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x5400, 0x00005401, 0x0001, 0 ); // coef write mask 710 + + // AppleHDATDMBusManagerCS8409::disableTDMPath + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000220, 0 ); // coef read 716 + + new_coef1 = (ret_coef1 & 0xffdf); // clear out 0x20 bit + myprintk_dbg("snd_hda_intel: masked cs_8409_disable_TDM_proper_amps34 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 720 ); // coef write 720 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 720 + + // AppleHDATDMBusManagerCS8409::setupTDMPath or disableTDMPath calls AppleHDATDMBusManagerCS8409::configureTDMUR + // AppleHDATDMBusManagerCS8409::configureTDMUR only place calls this + // this is AppleHDATDMBusManagerCS8409::tdmInUse + // which reads from 0x19 to 0x57 in a loop if the snd_hda_coef_item returns 0 till the read value + // does not have the word sign bit set (ie 0x8000) or finish all 0x57 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 724 ); // coef read 724 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 728 ); // coef read 728 + // .... + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0056, 0x0000, 0x00008000, 968 ); // coef read 968 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0057, 0x0000, 0x00008000, 972 ); // coef read 972 + + tdm_in_use(codec, 40); + + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // coef write 977 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00000000); + +} + +static void cs_8409_disable_TDM_amps34(struct hda_codec *codec) +{ + int retval; + + cs_8409_disable_TDM_proper_amps34(codec); + + + // set to defaults and disable output + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00370600 +// snd_hda: conv stream channel map 3 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00320000 +// snd_hda: stream format 3 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 986); // 0x025f0700 + retval = snd_hda_codec_read(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02570700 +// snd_hda: 37 [] + +} + +static void playstop_disable_TDM_amps34(struct hda_codec *codec) +{ + cs_8409_disable_TDM_amps34(codec); +} + + +static void cs_8409_disable_amp_max(struct hda_codec *codec, int amp_address) +{ + //int retval; + + // interrupt read/state read new as of June 2019 + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5000 i2c data 0x0000 reg anal: GlobalEnable : Disable +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0300 i2c data 0x0300 reg anal: InterruptState0 +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0400 i2c data 0x0400 reg anal: InterruptState1 +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0c00 i2c data 0x0c00 reg anal: State1 + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0050, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x0003, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x000c, 0); // snd_hda + +} + + +static void cs_8409_disable_amp_ssm3(struct hda_codec *codec, int amp_address) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x2c i2c reg 0x0001 i2c data 0x0001 reg anal: PowerControl : PowerDown BVSenseOn + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0000, 0x0001, 1); // snd_hda + +} + +static void cs_8409_disable_amp_tas576(struct hda_codec *codec, int amp_address) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xd8 i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, amp_address, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp0(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xd8 i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xd8, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp1(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xda i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xda i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xda, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp2(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xdc i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xdc, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp3(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xde i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xde i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xde, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0001, 0x00fc, 0); // snd_hda + +} + + + +static void playstop_disable_amp_max(struct hda_codec *codec, int amp_address) +{ + cs_8409_disable_amp_max(codec, amp_address); +} + + +static void cs_8409_inputs_power_nids_on(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 2785); // 0x022f0500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 2786); // 0x023f0500 + + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + +} + +static void cs_8409_inputs_power_nids_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10741); // 0x022f0500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10742); // 0x023f0500 + + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec); + +static void cs_8409_headset_amp_format_setup_disable(struct hda_codec *codec, int full); + +static void cs43l83_headset_amp_format_setup(struct hda_codec *codec, int set_stream_id, int full); + +static void cs_8409_perform_external_device_unsolicited_responses(struct hda_codec *codec); + +static void cs_8409_plugin_complete_detect(struct hda_codec *codec); + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec); + +static void cs42l83_headset_mike_format_setup_enable(struct hda_codec *codec, int nullformat, int full); + +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec); + + +static int cs_8409_boot_setup_real(struct hda_codec *codec) +{ + int headset_on_boot = 0, retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //hda_nid_t beep_nid = spec->beep_nid; + + mycodec_info(codec, "command cs_8409_boot_setup_real start\n"); + + + setup_reset_and_clear(codec); + + // read parameters from all nodes - excluding VirtualWidgets + + // the loop over node counts calls AppleHDAWidgetFactory::createAppleHDAWidget(DevIdStruct*) + // which Im assuming calls the initForNodeID functions + + init_read_all_nodes(codec); + + read_vendor_node(codec); + + + init_read_coefs(codec); + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + read_virtual_widgets(codec); + + init_for_node_vendor(codec); + + // this is determineSpeakerID + // this does not make sense - this just checks GPIO pin(s)?? + + determine_speaker_id(codec); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + + // why is this commented?? + //setup_jack_pin_config(codec); + + enable_i2c(codec); + + if (codec->core.subsystem_id == 0x106b3900) { + enable_GPIforUR(codec, 0x5); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + enable_GPIforUR(codec, 0xd); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + enable_GPIforUR(codec, 0xd); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + + if (codec->core.subsystem_id == 0x106b3900) { + cs42l83_external_control_GPIO(codec, 0x7); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // we have confirmed that the disabling all the cs42l83 setup does not affect the amps + + cs42l83_reset(codec); + + //cs42l83_external_control_GPIO2_clear_2(codec); + //cs42l83_external_control_GPIO2_set_2(codec); + + if (codec->core.subsystem_id == 0x106b3900) { + cs42l83_external_control_GPIO(codec, 0x7); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + cs42l83_device_id(codec); + + cs42l83_inithw(codec); + + + if (codec->core.subsystem_id == 0x106b3900) { + //setup_amps_reset(codec); + setup_amps_reset_i2c_max(codec); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_amps_reset_i2c_ssm3 + setup_amps_reset_i2c_ssm3(codec); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //setup_amps_reset(codec); + setup_amps_reset_i2c_tas576(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + //read_gpio_status(codec); + + retval = read_gpio_status_check(codec); + + + cs42l83_mic_detect(codec); + + // apparently the imacs use an inverted circuit for physical sensing of jack being plugged in + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + cs42l83_tip_sense(codec, 1); + else + cs42l83_tip_sense(codec, 0); + + cs42l83_plugin_interrupt_setup(codec); + + + // should check here if have headphones plugged in + // likely additional code here if we did boot with headphones plugged in + //cs42l83_headphone_sense_data(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real headphone ALREADY PLUGGED IN!!\n"); + // store for after init + headset_on_boot = 1; + } + + // cs_8409_intmike_format_setup_enable is done before we get a capture prepare + // so we need to store some dummy initial setup + // unlike OSX make it a stream id of 0 + // (note this only updates our local cached data - need a cs_8409_really_update_stream_format to actually update nid) + cs_8409_store_stream_format(codec, spec->intmike_adc_nid, 0, 0x4031); + + //setup_intmike_nid(codec); + //cs_8409_intmike_format_setup_format(codec); + cs_8409_intmike_format_setup_enable(codec, 0x4031, 1); + cs_8409_intmike_volume_setup(codec, 0x27); + cs_8409_intmike_stream_on_nid(codec); + + //setup_intmike_format_resetup(codec); + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + //setup_linein_nid(codec); + cs_8409_linein_volume_setup(codec, 0x27); + cs_8409_linein_format_setup_disable(codec); + + //setup_intmike_stream_conn_off_nid(codec); + cs_8409_intmike_stream_conn_off(codec); + + //setup_linein_stream_conn_off_nid(codec); + cs_8409_linein_stream_conn_off(codec); + + //setup_intmike_stream_off_nid(codec); + cs_8409_intmike_stream_off_nid(codec); + + //setup_linein_stream_off_nid(codec); + cs_8409_linein_stream_off_nid(codec); + + //setup_intmike_vol1(codec); + cs_8409_intmike_volume_mute_nouse(codec); + + //setup_linein_vol1(codec); + cs_8409_linein_volume_mute_nouse(codec); + + //setup_intmike_vol2(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol2(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + if (headset_on_boot) + { + // so the big question is what to do about unsolicited responses + // should we disable them in this boot section + // and if so when should we re-enable them + // I suspect at some point the code for headset already plugged in + // switches to unsolicited response code + + // so I think we need to enable unsol response at this stage and block it till end of this function + // - because we seem to get an UNSOL response in the middle of this code + spec->block_unsol = 1; + + // Im guessing the following code should be done explicitly + // because we have not seen a cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int + + // for some reason the stream id is set to 0 here + cs43l83_headset_amp_format_setup(codec, 0, 0); + + //#2880: cs42l83_configure_int_mclk + //#3152: cs42l83_power_onoff + //#3216: cs42l83_configure_serial_port + //#3376: cs42l83_output_set_input_sample_rate + //#3504: cs42l83_setup_audio_output + //#3680: cs42l83_buffers_onoff + + // this is essentially from cs42l83_headset_play_setup_on + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + // so we seem to get an UNSOL response at this point + // - Im thinking we need to store this response + + + // and now turn off - this is now cs42l83_headset_disable + + //#3698: cs42l83_buffers_onoff + //#3714: cs42l83_headset_power_off + + cs42l83_buffers_onoff(codec, 0); + + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // and back on again + // we dont set stream id, do full TDM setup and not enable the pin + // then set stream id, turn on pin but partial TDM setup + cs43l83_headset_amp_format_setup(codec, 0, 1); + cs43l83_headset_amp_format_setup(codec, 1, 0); + + //#3920: cs42l83_configure_int_mclk + //#4192: cs42l83_power_onoff + //#4256: cs42l83_configure_serial_port + //#4416: cs42l83_output_set_input_sample_rate + //#4544: cs42l83_setup_audio_output + //#4720: cs42l83_buffers_onoff + + // this is essentially from cs42l83_headset_play_setup_on + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + //#4738: cs42l83_headphone_sense + + // this may be the same as one of the multiple headphone sense calls seen if no headset plugged in + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real headphone still plugged in!!\n"); + + cs42l83_buffers_onoff(codec, 0); + + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + + cs_8409_intmike_volume_unmute_nouse(codec); + + cs_8409_linein_volume_unmute_nouse(codec); + + cs_8409_intmike_volume_unmute_nouse(codec); + + cs_8409_linein_volume_unmute_nouse(codec); + + cs_8409_inputs_power_nids_off(codec); + + + // so I think we need to try performing the unsol responses here + // - because we get a call to cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int here + // well all the evidence is altho these routines are called they dont actually do anything + // - the cs_8409_read_status_and_clear_interrupt call does not see a TIP SENSE interrupt + // (cf actually plugging in headphones post-boot) + // - calling these routines does clear the interrupt - which may be its only function + + // we need to update headset_phase so we dont ignore headset unsol responses in the following call + spec->headset_phase = 1; + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + spec->block_unsol = 1; + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED - UNIMPLEMENTED!!\n"); + } + + + // so now just going to continue with the logged boot code + // we get a lot of checks the headset is still plugged in + // Im assuming if these detect the headset has been removed other things may happen + // - this would likely be setting up the amps + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 2 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + cs_8409_intmike_volume_unmute(codec); + + cs_8409_linein_volume_unmute(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 3 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + //#5328: cs42l83_headset_button_detect_interrupts_off + //#5392: cs42l83_headset_set_hpout_clamp_disable + + // these 2 functions seem to be this function + // - additional code is setting jack_present and an msleep + cs_8409_plugin_handle_detect(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 4 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + // so there are 4 gpio status reads here + // why 4?? + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + // why - this just sets nid 0x22 format??? + cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + + //cs_8409_linein_volume_setup_new(codec, 0x27) + cs_8409_linein_volume_setup(codec, 0x27); + + + cs_8409_linein_format_setup_disable(codec); + + cs_8409_intmike_stream_conn_off(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + cs_8409_linein_stream_off_nid(codec); + + cs_8409_intmike_volume_mute(codec); + cs_8409_linein_volume_mute(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 5 - UNIMPLEMENTED!!\n"); + } + + // we see a delay here - this is I think the equivalent of the 1800 msleep + // in cs_8409_headset_plugin_event + // should we just make it 1800?? + msleep(1500); // what we actually see is 1470 + + // the cs_8409_plugin_complete_detect function seems to encapsulate what happens next + // - this functions ends with initiating headset detection + //#5551: cs42l83_headphone_sense + //#5567: cs42l83_complete_jack_detect + //#5631: cs42l83_power_hs_bias_on + //#5727: cs42l83_enable_hs_auto_int_on + //#5759: cs42l83_unplug_interrupt_setup + //#5807: cs42l83_set_hpout_pulldown_off + //#5839: cs42l83_headset_detect_on + + cs_8409_plugin_complete_detect(codec); + + spec->block_unsol = 1; + + // testing status checks to see if we get an interrupt + // so at this point the status is still 0x27 - no interrupt + // can we just use the msleep?? + retval = read_gpio_status_check(codec); + + msleep(1); + + // but here the status is 0x26 ie interrupt + //retval = read_gpio_status_check(codec); + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + + // so next we should get some unsol responses + //#5923: cs42l83_read_status_and_clear_interrupt + //#6117: cs42l83_disambiguate_ur_from_int + //#6165: cs_8409_headset_type_detect_event + //#6165: cs42l83_enable_hs_auto_int_off + //#6197: cs42l83_headset_type + //#6213: cs42l83_unplug_headset_detect_off + //#6277: cs42l83_set_hpout_pulldown_onoff + //#6309: cs42l83_set_hpout_clamp_enable + //#6341: cs42l83_enable_hsbias_auto_clamp_on + //#6373: cs42l83_enable_hsbias_auto_clamp_off + //#6421: cs42l83_power_hs_bias_off + //#6517: cs42l83_setup_button_detect + //#6709: cs42l83_power_hs_bias_button_on + //#6805: cs42l83_enable_hsbias_auto_clamp_off1 + + cs_8409_perform_external_device_unsolicited_responses(codec); + + + retval = read_gpio_status_check(codec); + + // so the delay here seems to be around 130 ms + // with delay of 1 ms dont see any interrupts + msleep(130); + + retval = read_gpio_status_check(codec); + + + //#6857: cs42l83_read_status_and_clear_interrupt + //#7051: cs42l83_disambiguate_ur_from_int + // cs_8409_headset_button_detect_event + //#7099: cs42l83_handle_button_detect + //#7243: cs42l83_mike_connected + + // cs_8409_perform_external_device_unsolicited_responses then calls cs_8409_plugin_event_continued + // - but here we have a divergence from plugin post-boot + // fixed up cs_8409_plugin_event_continued to only do things for plugin post-boot + // - at boot we drop back to here + // NOTA BENE - MUST set up the button interrupts here now - otherwise buttons wont work + + cs_8409_perform_external_device_unsolicited_responses(codec); + + + //#7553: cs_8409_enable_headset_streaming + //#7553: cs43l83_headset_amp_format_setup + //#7417: cs42l83_headset_play_setup_on + //#7417: cs42l83_configure_int_mclk + //#7689: cs42l83_power_onoff + //#7753: cs42l83_configure_serial_port + //#7913: cs42l83_output_set_input_sample_rate + //#8041: cs42l83_setup_audio_output + //#8217: cs42l83_buffers_onoff + + cs43l83_headset_amp_format_setup(codec, 1, 1); + cs42l83_headset_play_setup_on(codec); + + // and yet again turn off + //#8233: cs42l83_buffers_onoff + //#8249: cs42l83_power_onoff + //#8279: cs_8409_headset_amp_format_setup_disable + + cs42l83_buffers_onoff(codec, 0); + cs42l83_power_onoff(codec, 0); + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // and back on again + // we dont set stream id, do full TDM setup and not enable the pin + // then set stream id, turn on pin but partial TDM setup + cs43l83_headset_amp_format_setup(codec, 0, 1); + cs43l83_headset_amp_format_setup(codec, 1, 0); + + + //#8455: cs42l83_headset_play_setup_on + //#8455: cs42l83_configure_int_mclk + //#8727: cs42l83_power_onoff + //#8791: cs42l83_configure_serial_port + //#8951: cs42l83_output_set_input_sample_rate + //#9079: cs42l83_setup_audio_output + //#9255: cs42l83_buffers_onoff + + cs42l83_headset_play_setup_on(codec); + + + //#9273: cs42l83_headphone_sense + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + //#9287: cs_8409_intmike_stream_conn_off_disable + //#9299: cs_8409_linein_stream_conn_off + + cs_8409_intmike_stream_conn_off(codec); + cs_8409_linein_stream_conn_off(codec); + + // snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + // snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + // snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + // snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + + //#9324: cs_8409_intmike_volume_setup + //#9357: cs_8409_intmike_format_setup_disable + //#9324: cs_8409_linein_volume_setup + //#9401: cs_8409_linein_format_setup_disable + //#9412: cs42l83_headset_mike_format_setup_enable + //#9451: cs42l83_input_set_output_sample_rate + //#9579: cs42l83_mike_setup_audio_input + //#9643: cs42l83_mike_enable + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_format_setup_disable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + + + // as the following relates to the headset mike + // Im guessing we should only do it if we have a mike + // spec->have_mike should have been set above + // by the unsol response function cs_8409_headset_type_detect_event + + if (spec->have_mike) + { + + // this is part of cs_8409_enable_headset_mike_streaming + + // whats missing is no actual cs42l83 power on calls + // or unmuting input + + cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + cs42l83_input_set_output_sample_rate(codec); + + cs42l83_mike_setup_audio_input(codec); + + cs42l83_mike_enable(codec); + + + // and disable it all!! + // this is part of cs_8409_disable_headset_mike_streaming + // as usual disabling this duplicate setup + + //#9675: cs42l83_mike_disable + //#9705: cs42l83_headset_mike_format_setup_disable + + //cs42l83_mike_disable(codec); + + //cs42l83_headset_mike_format_setup_disable(codec); + + + //#9738: cs42l83_headset_mike_format_setup_enable + //#9738: cs42l83_headset_mike_format_setup_enable + //#9810: cs42l83_input_set_output_sample_rate + //#9938: cs42l83_mike_setup_audio_input + //#10002: cs42l83_mike_enable + + // why duplicate setup - 1st dont set stream id/pin + // 2nd set stream id and pin + //cs42l83_headset_mike_format_setup_enable(codec, 0, 0); + //cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + //cs42l83_input_set_output_sample_rate(codec); + + //cs42l83_mike_setup_audio_input(codec); + + //cs42l83_mike_enable(codec); + + //// this doesnt make sense double read of nid 0x3c then double set?? + //retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 25649); // 0x03cf0700 + //retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 25650); // 0x03cf0700 + //snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + //// snd_hda: 60 ['AC_PINCTL_IN_EN'] + //snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + //// snd_hda: 60 ['AC_PINCTL_IN_EN'] + + // is this a good position to switch the inputs?? + // just before enable buttons is where we switch inputs on plugin + // note that this is just telling Alsa the input source has changed + // - no changes to audio setup at all + switch_input_src(codec); + + + //#10040: cs_8409_headset_mike_buttons_enable + //#10040: cs42l83_configure_headset_button_interrupts + //#10088: cs42l83_enable_hsbias_auto_clamp_off2 + //#10136: cs42l83_enable_hsbias_auto_clamp_on3 + + cs_8409_headset_mike_buttons_enable(codec); + + + // we get some calls to cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int here + // clear out any stored unsol responses + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + spec->block_unsol = 1; + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + } + + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 6 - UNIMPLEMENTED!!\n"); + } + + + spec->block_unsol = 0; + + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot CURRENT IMPLEMENTATION END!!\n"); + + } + else + { + + // NOTE - OSX sets a stream format here but a null (ie 0) stream id + // on linux we set the OSX format - it will be updated with actual stream format later + + //setup_TDM_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + //amps_enable2_6462(codec); + cs_8409_setup_amps12(codec, 0); + + //setup_TDM_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_enable2_7472(codec); + cs_8409_setup_amps34(codec, 0); + + //sync_converters(codec); + cs_8409_sync_converters_on(codec, 1); + + + //sync_converters1(codec); + cs_8409_sync_converters_off(codec, 1); + + //amps_disable_6462(codec); + cs_8409_disable_amps12(codec); + + //putative_tdm_disable_6462(codec); + cs_8409_disable_TDM_amps12(codec); + + //amps_disable_7472(codec); + cs_8409_disable_amps34(codec); + + //putative_tdm_disable_7472(codec); + cs_8409_disable_TDM_amps34(codec); + + + // I think Im going to disable the following as we appear to have a stream id here + // but under linux we do not have a stream at all at this boot stage + + if (0) + { + + // this is not quite correct - cs_8409_setup_TDM_amps12 will write a null stream id etc + // but the actual logged version does not - although does update format + // this may be because Apple is caching the stream format/id similar to linux and + // at this point we have already set a null stream id - but because the above disable also + // cleared the format we get a re-setup of the the format + //putative_enable1_TDM_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + //amps_disable2_6462(codec); + cs_8409_disable_amps12(codec); + + // see above notes for putative_enable1_TDM_6462 + //putative_enable1_TDM_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_disable2_7472(codec); + cs_8409_disable_amps34(codec); + + // so this does not set the channel id for node 0x03 to 0x2 but cs_8409_sync_converters_on does + // is this significant?? + // does suggest this function reads the initial stream id then rewrites at the end + //sync_converters2(codec); + cs_8409_sync_converters_on(codec, 1); + + + // so this also not quite same - we actually have a stream id here on OSX + // but at the boot stage dont think we have this in linux + //enable2_TDM2_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + + //amps_enable2_6462(codec); + cs_8409_setup_amps12(codec, 0); + + // see above + //enable2_TDM2_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_enable2_7472(codec); + cs_8409_setup_amps34(codec, 0); + + //sync_converters3(codec); + cs_8409_sync_converters_on(codec, 1); + + + // I dont get this - sync_converters3 sets the stream id/channel id to non-zero + // but here when we read the stream id/channel id its 0?? + //sync_converters4(codec); + cs_8409_sync_converters_off(codec, 1); + + //amps_disable3_6462(codec); + cs_8409_disable_amps12(codec); + + //putative_disable3_TDM_6462(codec); + cs_8409_disable_TDM_amps12(codec); + + //amps_disable3_7472(codec); + cs_8409_disable_amps34(codec); + + //putative_disable3_TDM_7472(codec); + cs_8409_disable_TDM_amps34(codec); + } + + + // this is best guess what these volume functions are doing + // as from the log there is no change in output volume or muting + // - but if already unmuted thats what you would expect + + //setup_mic_vol2(codec); + + //setup_intmike_vol3(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol3(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + + //setup_mic_vol3(codec); + + //setup_intmike_vol4(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol4(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + + //setup_mic_vol3(codec); + //setup_input_power_nids_off(codec); + cs_8409_inputs_power_nids_off(codec); + + + //read_gpio_status1(codec); + + //read_gpio_status2(codec); + + //read_gpio_status3(codec); + + // why 3 reads here - they seem to return the exact same data each time + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + + //cs42l83_headphone_sense1(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 1 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + + //setup_mic_vol4(codec); + + //setup_intmike_vol5(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol5(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + //setup_mic_vol5(codec); + + //setup_intmike_vol6(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol6(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + //cs42l83_headphone_sense2(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 2 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + + //cs42l83_headphone_sense3(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 3 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + //setup_intmike_nid1(codec); + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + //setup_linein_vol7(codec); + cs_8409_linein_volume_setup(codec, 0x27); + + //setup_linein_nid1(codec); + cs_8409_linein_format_setup_disable(codec); + + //setup_intmike_stream_conn_off_nid1(codec); + cs_8409_intmike_stream_conn_off(codec); + + //setup_linein_stream_conn_off_nid1(codec); + cs_8409_linein_stream_conn_off(codec); + + //setup_intmike_stream_off_nid1(codec); + cs_8409_intmike_stream_off_nid(codec); + + //setup_linein_stream_off_nid1(codec); + cs_8409_linein_stream_off_nid(codec); + + + //setup_intmike_vol10(codec); + //cs_8409_volume_set(codec, 0x22, 0x33); + cs_8409_volume_set(codec, spec->intmike_adc_nid, 0x33); + + //setup_linein_vol10(codec); + //cs_8409_volume_set(codec, 0x23, 0x33); + cs_8409_volume_set(codec, spec->linein_amp_nid, 0x33); + } + + mycodec_info(codec, "command cs_8409_boot_setup_real end\n"); + + return 0; +} + + + +static void cs_8409_play_real(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + //cs_8409_play_data(codec); + + mycodec_info(codec, "command cs_8409_play_real start"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + play_setup_TDM_amps12(codec, 1); + + play_setup_amps12(codec); + + + play_setup_TDM_amps34(codec); + + play_setup_amps34(codec); + + + play_sync_converters_on(codec); + + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_play_real end"); + +} + + + + +static void cs_8409_amps_disable_streaming(struct hda_codec *codec) +{ + //int retval; + mycodec_info(codec, "cs_8409_amps_disable_streaming start\n"); + + playstop_sync_converters_off(codec); + + playstop_disable_amps12(codec); + + playstop_disable_TDM_amps12(codec); + + playstop_disable_amps34(codec); + + playstop_disable_TDM_amps34(codec); + + // for some reason Apple duplicates the amp disable here?? + + playstop_disable_amps12(codec); + + playstop_disable_amps34(codec); + + + mycodec_info(codec, "cs_8409_amps_disable_streaming end\n"); +} + +static void cs_8409_playstop_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + mycodec_info(codec, "command cs_8409_playstop_real start"); + + //cs_8409_playstop_data(codec); + + cs_8409_amps_disable_streaming(codec); + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + // weird - why set the inputs to powered off when stop playing?? + cs_8409_inputs_power_nids_off(codec); + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_playstop_real end"); +} + + + +static void cs_8409_capture_real(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + //cs_8409_capture_data(codec); + + mycodec_info(codec, "command cs_8409_capture_real start"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + // NOTE - there are big ordering issues here + // - here we setup the speaker output before the internal mike + // - this maybe because Quicktime defaults to enabling play when recording + // unfortunately looks as tho linux tends to open the capture stream before the playback stream + // - so going to ignore this here + + + cs_8409_inputs_power_nids_on(codec); + + //cs_8409_intmike_format_setup_format(codec); + cs_8409_intmike_format_setup_enable(codec, 0x4031, 0); + + cs_8409_intmike_volume_setup(codec, 0x27); + cs_8409_intmike_stream_on_nid(codec); + + cs_8409_intmike_volume_unmute(codec); + cs_8409_linein_volume_unmute(codec); + + // so here we get AMP_GAIN_MUTE setups but nothing changes + // - so either this is a volume update with no change or unmute with no change + // - which to do with?? + //cs_8409_intmike_volume_setup - (no change) + //cs_8409_linein_volume_setup - (no change) + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_capture_real end"); + +} + +static void cs_8409_intmike_stream_reset_nid_on(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // OK dont get this - we turn the stream back on for the internal mike + // - but assume pin is OK?? + // now think these 2 functions are resetting to original state - which happens + // to be stream on for intmike and stream off for linein + // NOT PROPERLY FUNCTIONAL YET!!!!! + +// retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 23); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 1)] + +} + +static void cs_8409_linein_stream_reset_nid_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // OK dont get this - we turn the stream off for the linein + // - but assume pin is OK?? + +// retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 25); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + +} + + +static void cs_8409_capturestop_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + mycodec_info(codec, "command cs_8409_capturestop_real start"); + + //cs_8409_capturestop_data(codec); + + + cs_8409_intmike_stream_conn_off(codec); + cs_8409_linein_stream_conn_off(codec); + + // this I think is re-setting to pre-capture state + // THIS NEEDS FIXING!!! + cs_8409_intmike_stream_reset_nid_on(codec); + cs_8409_linein_stream_reset_nid_off(codec); + + + cs_8409_intmike_volume_set(codec, 0x27); + cs_8409_intmike_volume_mute(codec); + + // we also reset the pin - however no change to volume or unmute + // so cant say if should just set unmute or set volume to 0 + // just choosing one + //cs_8409_volume_set(codec, 0x44, 0x00); + cs_8409_volume_set(codec, spec->intmike_nid, 0x00); + + + cs_8409_intmike_format_setup_disable(codec); + + + cs_8409_linein_volume_set(codec, 0x27); + cs_8409_linein_volume_mute(codec); + + // we also reset the pin - however no change to volume or unmute + // so cant say if should just set unmute or set volume to 0 + // just choosing one + //cs_8409_volume_set(codec, 0x45, 0x00); + cs_8409_volume_set(codec, spec->linein_nid, 0x00); + + + cs_8409_linein_format_setup_disable(codec); + + + cs_8409_inputs_power_nids_off(codec); + + + // using Quicktime we get a play disable when we stop recording + //cs_8409_sync_converters_off + //cs_8409_disable_amps12 + //cs_8409_disable_TDM_amps12 + //cs_8409_disable_amps34 + //cs_8409_disable_TDM_amps34 + //cs_8409_disable_amps12 + //cs_8409_disable_amps34 + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_capturestop_real end"); +} + + +// this is the function which handles unsolicited responses +// - which seem to be enabled by default on the 8409 and set to be from GPIO pin 1 +// which seems to be handling the cs42l83 + +static int cs_8409_read_status_and_clear_interrupt(struct hda_codec *codec); +static int cs42l83_disambiguate_ur_from_int(struct hda_codec *codec); +static void cs_8409_interrupt_action(struct hda_codec *codec, int int_response); + +static void cs_8409_external_device_unsolicited_response(struct hda_codec *codec) +{ + int retval; + int retval_int; + int ret_disambig; + int int_response; + int int_masked; + int intcnt = 0; + + retval = read_gpio_status_check(codec); + + if ((retval & 0x01) == 0x01) + { + mycodec_info(codec, "cs_8409_external_device_unsolicited_response - interrupt clear\n"); + return; + } + + mycodec_info(codec, "cs_8409_external_device_unsolicited_response - interrupt set\n"); + + // so retval_int is a bit shifted combination of a number of primary interrupt status registers + + retval_int = cs_8409_read_status_and_clear_interrupt(codec); + + // and ret_disambig is the same bit shifted combination of a number of primary interrupt mask registers + + ret_disambig = cs42l83_disambiguate_ur_from_int(codec); + + // move prints to after so not spaced by other prints + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL interrupt 0x%08x\n",retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL disambig 0x%08x\n",ret_disambig); + + // determine masked interrupts + + int_masked = (ret_disambig & retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL masked 0x%08x\n",int_masked); + + // determine unmasked interrupts + + int_response = ((~ret_disambig) & retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL unmasked 0x%08x\n",int_response); + + intcnt = hweight_long(int_response); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL number interrupt actions %d\n", intcnt); + + + // do we call a mapping function here?? + + if (int_response != 0) { + + cs_8409_interrupt_action(codec, int_response); + + } + else + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL NO unmasked interrupt\n"); + + + return; +} + + +static int cs42l83_read_status_and_clear_interrupt(struct hda_codec *codec); + +static int cs_8409_read_status_and_clear_interrupt(struct hda_codec *codec) +{ + int retval = 0; + int retint = 0; + int last_retint = 0; + int loopmax = 11; + int loopcnt = 0; + + // AppleHDAFunctionGroupCS8409::readStatusAndClearInterrupt + + mycodec_info(codec, "cs_8409_read_status_and_clear_interrupt start\n"); + + retval = read_gpio_status_check(codec); + + if ((retval & 0x01) == 0x01) + { + mycodec_info(codec, "cs_8409_read_status_and_clear_interrupt - interrupt %d clear\n",loopcnt); + return 0; + } + + // so this function causes an unsol response because of clearing the interrupt + // what this means is we need to add a check in the unsol response callbacks + // to ignore GPIO 1 changes from 0 to 1 - 1 seems to be the default + // and a 1 to 0 transition means interrupt has been triggered + + while (1) + { + + retint = cs42l83_read_status_and_clear_interrupt(codec); + + codec_info(codec, "cs_8409_read_status_and_clear_interrupt - UNSOL status 0x%08x\n",retint); + + retval = read_gpio_status_check(codec); + + if (loopcnt >= loopmax) + { + dev_info(hda_codec_dev(codec), "cs_8409_read_status_and_clear_interrupt - ERROR - max count exceeded\n"); + break; + } + + loopcnt++; + + if ((retval & 0x01) == 0x01) + { + codec_info(codec, "cs_8409_read_status_and_clear_interrupt - interrupt %d clear\n",loopcnt); + break; + } + + last_retint = retint; + + // so this code definitely has a 10 IOSleep sleep call ie 10 ms + // but from the logs it is much closer to 50 ms + // IOSleep(10); + //usleeprange(10000,12000); + msleep(10); + + } + + codec_info(codec, "cs_8409_read_status_and_clear_interrupt 0x%08x end\n",retint); + + return retint; +} + +static int cs42l83_read_status_and_clear_interrupt(struct hda_codec *codec) +{ + int retval; + int retval_det1; + int retval_det2; + int retval_cdc; + + // AppleHDATDM_CS42L83::readStatusAndClearInterrupt + + // so I think Ive finally figured whats going on with interrupts and the 8409 + // cs42l83 interrupts trigger a state change on GPIO pin 1 which is set to + // trigger an Unsolicited Response (UR) (enableGPIforUR function) + // so we get a UR from high(1) (default) to low(0) when the interrupt is set + // plus a UR from low(0) to high(1) when the interrupt is cleared + // interrupt clearing seems to be triggered by reading registers in this routine (hence the name) + // Im now pretty certain 0x1b7b and 0x1b7c indicate which interrupt of + // 0x1b79 and 0x1b7a (Detect Interrupt Mask 1 and Detect Interrupt Mask 2) + // was triggered + + // Im suspecting the 0x1b7b (maybe 0x1b7c) which are undocumented + // - the other registers seem to be flagged as status registers + // 0x1b7b is listed in figure 4-45 as an interrupt register but otherwise undocumented!! + + // moved to above + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // note that a lot of i2cPagedRead followed by i2cPagedWrite are likely AppleHDATDMDevice::maskWriteReg + // ie read from register and mask of bits and set certain bits without affecting others + // we do not know the mask being used from the HDA commands!! + + // finally know what ASP refers to in lot of Cirrus docs - Audio Serial Port + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Detect Interrupt 1 Status + // Detect Interrupt 1 Status + // value 0x40 (TIP_SENSE_PLUG interrupt from 0xa0 TIP_SENSE_PLUG unmasked) + // register 0x1b7c - this is undocumented for 42l42 (reserved) + // Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xff state of 0x1b7a) + // register 0x1308 - Codec Interrupt Status + // value 0x01 - Headset disabled, Powered down + // register 0x1301 - ADC Overflow Interrupt Status + // register 0x1302 - Mixer Interrupt Status + // register 0x1303 - SRC Interrupt Status + // register 0x1304 - ASP RX Interrupt Status + // register 0x1305 - ASP TX Interrupt Status + // register 0x130b - SRC Partial Lock Interrupt Status + // register 0x130d - VP Monitor Interrupt Status + // register 0x130e - PLL Lock Interrupt Status + // register 0x130f - Tip/Ring Sense Plug/Unplug Interrupt Status + // value 0x00 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b40 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 + + mycodec_info(codec, "cs42l83_read_status_and_clear_interrupt start\n"); + + // note that these functions contain power on/power off calls + + retval_det1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval_det2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + retval_cdc = cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "cs42l83_read_status_and_clear_interrupt end\n"); + + retval = 0; + retval = ((retval_det1 & 0xff) << 16) | ((retval_det2 & 0xff) << 8) | (retval_cdc & 0xff); + + return retval; +} + +static int cs42l83_disambiguate_ur_from_int(struct hda_codec *codec) +{ + int retval_det1; + int retval_det2; + int retval_cdc; + int retval; + + // from AppleHDAFunctionGroupCS8409::externalDeviceUnsolicitedResponse + + // AppleHDATDM_CS42L83::disambiguateURfromINT + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // register 0x1b79 - Detect Interrupt Mask 1 + // value 0xa0 TIP_SENSE_PLUG unmasked + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xff all masked + // register 0x131b - Codec Interrupt Mask + // value 0x3 all masked + + mycodec_info(codec, "cs42l83_disambiguate_ur_from_int start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79a0 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 + + retval_det1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + retval_det2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + retval_cdc = cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + + mycodec_info(codec, "cs42l83_disambiguate_ur_from_int end\n"); + + // there is a bunch of code here presumably figuring out what happened + + retval = 0; + retval = ((retval_det1 & 0xff) << 16) | ((retval_det2 & 0xff) << 8) | (retval_cdc & 0xff); + return retval; + +} + + +static void cs42l83_set_power_state_on(struct hda_codec *codec, int instate) +{ + int retval; + int loopcnt; + + mycodec_info(codec, "cs42l83_set_power_state_on start\n"); + + // likely in AppleHDATDM_CS42L83::enable + // (only place AppleHDATDM_CS42L83::setPowerState is called from is AppleHDATDM_CS42L83::enable) + // AppleHDATDM_CS42L83::setPowerState + + // register 0x1101 - Power Down Control 1 + // register 0x130b - SRC Partial Lock Interrupt Status + + // seems to me this powers on either the input path ie mike (instate=1) + // or output path ie headphones (instate=0) + + // I think that the use of cs_8409_vendor_i2cWriteMask means that we can + // call this function multiple times and set both input and output power on + // because only the bits in the mask are affected + +// codec headphone (digital in/audio out) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01fe +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + +// codec capture (audio in/digital out) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0196 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0116 i2c data 0x0016 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b24 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0116 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0112 i2c data 0x0012 + + + if (instate) + { + // power setup for codec (0x01) and output (0x80) + + // this converts 0xfe to 0x7e (or 0x96 to 0x16) + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x81, 0x0, 1); // snd_hda + + loopcnt = 0; + while (loopcnt < 0x14) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + if ((retval & 0x1) == 0x1) + break; + loopcnt++; + // IOSleep(2); + msleep(2); + } + + // power setup for ADC (0x04) + + // this converts 0x7e to 0x7a (or 0x16 to 0x12) + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x04, 0x0, 1); // snd_hda + } + else + { + // power setup for codec (0x01), input (0x40) and mixer (0x20) + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + // this converts 0xfe to 0x9e + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x61, 0x0, 1); // snd_hda + + loopcnt = 0; + while (loopcnt < 0x14) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + if ((retval & 0x4) == 0x4) + break; + loopcnt++; + // IOSleep(2); + msleep(2); + } + + // power setup for headphone (0x08) + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + + // this converts 0x9e to 0x96 + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x08, 0x0, 1); // snd_hda + + } + + mycodec_info(codec, "cs42l83_set_power_state_on end\n"); +} + +static void cs42l83_headset_amp_setup_TDM_sample_rate(struct hda_codec *codec) +{ + +// snd_hda: # AppleHDATDMBusManagerCS8409::setSampleRate: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0xffff, 0x00000200, 0, 3892 ); // coef write mask 3892 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0042, 0xffff, 0x00000040, 0, 3898 ); // coef write mask 3898 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0xffff, 0x000010ff, 0, 3904 ); // coef write mask 3904 + + // we need to use proper masked versions here - in particular for register 1 which seems to be enabling the Audio Serial Port + // for the subsystems and bits 0x7f need to pass thro here + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x0380, 0x00000200, 0, 0 ); // coef write mask 25 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0042, 0x0007, 0x00000040, 0, 0 ); // coef write mask 3898 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0x01ff, 0x000010ff, 0, 0 ); // coef write mask 3904 + +} + +static void cs42l83_headset_amp_setup_TDM_proper(struct hda_codec *codec, int full) +{ + //int retval; + int ret_coef0 = 0; + int new_coef0 = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + + if (full) + { + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 3810 + new_coef1 = (ret_coef1 & 0xffff); // not clear what this is setting - no difference between read and write so far + // however if used in different places the actual value may be different + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_setup_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 3814 ); // coef write 3814 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 3814 ); // coef write 3814 + } + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0015, 0xaaaa, 0xffff, 0x0000aaaa, 0, 0 ); // coef write mask 3819 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0014, 0x0100, 0xffff, 0x00000000, 0, 0 ); // coef write mask 3825 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00008000, 0 ); // coef read 3832 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0029, 0x0800, 0x00000000, 0 ); // coef write 3836 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00008000, 0 ); // coef read 3840 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x002a, 0x0820, 0x00000000, 0 ); // coef write 3844 + + if (full) + { + ret_coef0 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 3848 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3852 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0007, 0x0000, 0x000028ff, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 3856 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3860 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3864 + // NOTA BENE - here we update from 0x9000 to 0xb000 - which is then never removed - even after unplugging headphones!! + new_coef0 = ret_coef0 | 0x2000; + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, new_coef0, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + + // snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0006, 0x8000, 0xffff, 0x00008000, 0, 0 ); // coef write mask 3868 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0040, 0xffff, 0x00000000, 0, 0 ); // coef write mask 3874 + + // snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + // snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0xa801, 0xffff, 0x00000001, 0, 3880 ); // coef write mask 3880 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x0000, 0x00000001, 0xa801, 0 ); // coef write mask 3880 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0002, 0x0280, 0xffff, 0x00000280, 0, 0 ); // coef write mask 3886 + + cs42l83_headset_amp_setup_TDM_sample_rate(codec); + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 3910 + + new_coef1 = (ret_coef1 & 0xffff) | 0x40; + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_setup_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0240, 0x00000000, 3914 ); // coef write 3914 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 3914 + } + + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 3918 ); // coef read 3918 + + tdm_in_use(codec, 1); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3987 + + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 3991 + + new_coef71 = (ret_coef71 & 0xffff) | 0x800f; + myprintk_dbg("snd_hda_intel: masked play_setup_TDM_proper_full_headphone coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 3995 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3995 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3995 + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 3998 + +} + +static void cs43l83_headset_amp_format_setup(struct hda_codec *codec, int set_stream_id, int full) +{ + int retval; + + mycodec_info(codec, "cs43l83_headset_amp_format_setup set_stream_id %d full %d\n", set_stream_id, full); + + //snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x00a24031 +// snd_hda: stream format 10 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + if (set_stream_id) + { + //snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00a70610 + // snd_hda: conv stream channel map 10 [('CHAN', 0), ('STREAMID', 1)] + + // using the stored stream parameters update nid 0x0a stream parameters + // we have limited the allowed formats so should only have working formats here + cs_8409_really_update_stream_format(codec, 0x0a, 1, 2, 0); + } else { + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00a70600 + } + + //cs42l83_headset_amp_setup_TDM_proper_full(codec); + cs42l83_headset_amp_setup_TDM_proper(codec, full); + + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x02cf0700 + + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x000000c0); // 0x02c707c0 +// snd_hda: 44 ['AC_PINCTL_OUT_EN', 'AC_PINCTL_HP_EN'] + +} + +static void cs_8409_headset_amp_disable_TDM_proper(struct hda_codec *codec, int full) +{ + int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + // AppleHDATDMBusManagerCS8409::disableTDMPath + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00000800, 0 ); // coef read 2411 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0029, 0x8800, 0x00000000, 0 ); // coef write 2415 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00000820, 0 ); // coef read 2419 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x002a, 0x8820, 0x00000000, 0 ); // coef write 2423 + + if (full) + { + // AppleHDATDMBusManagerCS8409::disableTDMPath + // snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0001, 0xffff, 0x0000a801, 0, 2185 ); // coef write mask 2185 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0xa800, 0x0000a801, 0x0001, 0 ); // coef write mask 2185 + + // AppleHDATDMBusManagerCS8409::disableTDMPath + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000240, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef read 2191 + + new_coef1 = (ret_coef1 & 0xffbf); // clear our 0x40 bit + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 2195 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 2195 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 2195 + } + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 2427 ); // coef read 2427 + + tdm_in_use(codec, 301); + + if (full) + { + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // coef write 2452 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00000000); + } + else + { + + // snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 2624 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 2628 + + new_coef71 = (ret_coef71 & 0xffff); // why doesnt this really disable this here?? + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_TDM_proper coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 2632 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 2635 + + } + +} + +static void cs_8409_headset_amp_format_setup_disable(struct hda_codec *codec, int full) +{ + int retval; + + mycodec_info(codec, "cs_8409_headset_amp_format_setup_disable full %d\n", full); + + cs_8409_headset_amp_disable_TDM_proper(codec, full); + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00a70600 +// snd_hda: conv stream channel map 10 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00a20000 +// snd_hda: stream format 10 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 0); // 0x02cf0700 + + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02c70700 +// snd_hda: 44 [] + +} + + +static void cs42l83_headset_mike_format_setup_enable(struct hda_codec *codec, int nullformat, int full) +{ + int retval = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4031 is Apples fixed format - but this is for plugin stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 + if (full) + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 + } + else + { + if (full) + cs_8409_really_update_stream_format(codec, 0x1a, 1, 2, 0); + else + cs_8409_really_update_stream_format(codec, 0x1a, 1, 0, 0); + } + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000cccc, 0, 0 ); // coef write mask 12272 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000000, 0, 0 ); // coef write mask 12278 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008000, 0 ); // coef read 12285 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 12289 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008000, 0 ); // coef read 12293 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 12297 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 12301 ); // coef read 12301 + + tdm_in_use(codec, 201); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 12370 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 12374 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_mike_format_setup_enable coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 12378 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 12381 + + if (full) + { + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + // snd_hda: 60 ['AC_PINCTL_IN_EN'] + } + +} + +static void cs42l83_headset_mike_format_setup_disable(struct hda_codec *codec) +{ + int retval = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + +// snd_hda: # AppleHDATDMBusManagerCS8409::disableTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 13141 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x8800, 0x00000000, 0 ); // coef write 13145 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 13149 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x8820, 0x00000000, 0 ); // coef write 13153 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13157 ); // coef read 13157 + + tdm_in_use(codec, 202); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13226 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13230 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_mike_format_setup_disable coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 13234 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13234 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13234 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13237 + + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x01a70600 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x01a20000 +// snd_hda: stream format 26 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + +} + +// NOTE - the following routines NOT fixed up yet - currently not used!! + +static void cs42l83_headset_mike_format_setup_enable1(struct hda_codec *codec) +{ + //int retval = 0; + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + cs_8409_really_update_stream_format(codec, 0x1a, 1, 0, 0); + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13249 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13255 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008800, 0 ); // coef read 13262 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13266 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008820, 0 ); // coef read 13270 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13274 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13278 ); // coef read 13278 + + tdm_in_use(codec, 203); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13347 + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13351 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13355 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13358 + +} + +static void cs42l83_headset_mike_format_setup_enable1a(struct hda_codec *codec) +{ + //int retval = 0; + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13249 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13255 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008800, 0 ); // coef read 13262 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13266 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008820, 0 ); // coef read 13270 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13274 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13278 ); // coef read 13278 + + tdm_in_use(codec, 203); + +} + +static void cs42l83_headset_mike_format_setup_enable1b(struct hda_codec *codec) +{ + int retval = 0; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 1)] + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13368 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13374 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 13381 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13385 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 13389 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13393 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13397 ); // coef read 13397 + + tdm_in_use(codec, 204); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13466 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13470 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13474 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13477 + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + +} + +// NOTE - end of unfixed routines + + +static void cs42l83_headset_amp_disable_and_mike_disable_TDM_proper(struct hda_codec *codec) +{ + //int retval = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + +// snd_hda: # AppleHDATDMBusManagerCS8409::disableTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 10459 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x8800, 0x00000000, 0 ); // coef write 10463 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 10467 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x8820, 0x00000000, 0 ); // coef write 10471 + + // this section is actually disabling the headset amp TDM setup + +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5400, 0xffff, 0x0000fc00, 0, 10475 ); // coef write mask 10475 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0xa800, 0x0000fc00, 0x5400, 0 ); // coef write mask 10475 + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000260, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef read 10481 + + new_coef1 = (ret_coef1 & 0xffbf); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_and_mike_disable_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0220, 0x00000000, 10485 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 10485 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 10485 + + // end of section disabling the headset amp TDM setup + + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 10489 ); // coef read 10489 + + tdm_in_use(codec, 401); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10494 + + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000c00f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 10498 + + new_coef71 = (ret_coef71 & 0xffff); // I dont get this - it doesnt seem to change this at all - but this is for output 0x800f (headphones) or 0x400f (amps) + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_and_mike_disable_TDM_proper coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0xc00f, 0x00000000, 10502 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10502 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10502 + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 10505 + +} + +static void cs42l83_headset_amp_disable_and_mike_format_setup_disable(struct hda_codec *codec) +{ + int retval = 0; + + cs42l83_headset_amp_disable_and_mike_disable_TDM_proper(codec); + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x01a70600 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x01a20000 +// snd_hda: stream format 26 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x03cf0700 + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + +} + + +static void cs_8409_linein_format_setup_enable(struct hda_codec *codec) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // theres some weird issue here + // index 0x0009 has bit 0x0080 set only after an unplug event with headset with mike + // but then never seems to be turned off!!! + + // 0x45 -> 0x23 is line input + + + // now updated to not write the Apple format but use my format setting routines + // (remember we have limited the allowed formats to acceptable ones) + // note that apparently we can set the format with the nid powered down but for setting the + // stream id the nid has to be powered up + // we may wish to ignore the power down here - because we reactivate the nid only a few steps + // later + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02324033 +// snd_hda: stream format 35 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10555); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10558); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x02370612 +// snd_hda: conv stream channel map 35 [('CHAN', 2), ('STREAMID', 1)] + + + // using the stored stream parameters update nid 0x23 stream parameters + // we have limited the allowed formats so should only have working formats here + //cs_8409_really_update_stream_format(codec, 0x23, 1, 1, 0); + cs_8409_really_update_stream_format(codec, spec->linein_amp_nid, 1, 1, 0); + + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10561); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + +// snd_hda: # AppleHDAWidgetCS8409::setConnectionSelect: + ret_coef9 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x000000b3, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef read 10563 + //new_coef9 = ret_coef9 | 0x80; // I dont get this bit set - see above + new_coef9 = ret_coef9 | spec->reg9_linein_dmic_mo; // I dont get this bit set - see above + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_format_setup_enable coef 0x09 update 0x%04x 0x%04x \n", ret_coef9, new_coef9); + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0x00000000, 10567 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10567 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, new_coef9, 0x00000000, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10567 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02370100 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02370100 + +} + +static void cs_8409_linein_stream_on_nid(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00005401, 0 ); // coef read 10584 + + //new_coef82 = (reg_coef82 | 0x2); + new_coef82 = (reg_coef82 | spec->reg82_linein_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_stream_on_nid coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x5403, 0x00000000, 10588 ); // coef write 10588 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 10588 ); // coef write 10588 + + //retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + retval = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04570720 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04570720 +// snd_hda: 69 ['AC_PINCTL_IN_EN'] + +} + +static void cs_8409_intmike_stream_conn_off_disable(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // more weird issue here + // index 0x0009 has bit 0x0100 set only after an unplug event with headset with mike + // it is reset + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10596); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10599); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10602); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this is NOT from setConnectionSelect - unknown where from + // very not clear what this does - it appears as part of the multiple disable/enables +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x01b3, 0xffff, 0x000000b3, 0, 10604 ); // coef write mask 10604 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0100, 0x0000, 0x000000b3, 0x01b3, 0 ); // coef write mask 10604 + +} + + +static void cs_8409_intmike_stream_conn_off_enable(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10676); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10679); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10682); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this is NOT from setConnectionSelect - unknown where from + // very not clear what this does - it appears as part of the multiple disable/enables +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0xffff, 0x000001b3, 0, 10684 ); // coef write mask 10684 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0100, 0x000001b3, 0x00b3, 0 ); // coef write mask 10684 + +} + + +static void cs_8409_intmike_linein_resetup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + + // for some very strange reason we setup a 4 channel format after unplug of headset with mike + + //cs_8409_intmike_format_setup_format33(codec); + + cs_8409_intmike_format_setup_enable(codec, 0x4033, 1); + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_stream_on_nid(codec); + + cs_8409_linein_format_setup_enable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_stream_on_nid(codec); + + + cs_8409_intmike_stream_conn_off_disable(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + + + cs_8409_intmike_stream_conn_off_enable(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + + cs_8409_intmike_volume_mute(codec); + + cs_8409_linein_volume_mute(codec); + + cs_8409_intmike_volume_unmute(codec); + + cs_8409_linein_volume_unmute(codec); + + cs_8409_inputs_power_nids_off(codec); + +} + + +// this is where we need to decode the actions to be taken +// note that the button interrupts are undocumented for the cs42l42 (reserved) +// not yet clear which one is up and which one is down!! +// so after button detect 0x1b7b is 0x14 and 0x1b7c is 0x0a +// for 0x1b7b 0x14 are reserved bits for cs42l42 - but the 0x04 only seen on detection +// actual button presses are 0x01, 0x02 and button release 0x10 +// for 0x1b7c 0x02 is a short release for buttons, 0x08 is reserved +// the mask bits for 0x1b7a seem to be 0xe7 for buttons defining 0x08 as the button detect interrupt +// (0x1b79 is mask, 0x1b7b status; 0x1b7a is mask, 0x1b7c is presumed status, 0x131b is mask, 0x1308 status +// 0x1320 is mask, 0x130f status) +#define TIP_SENSE_PLUG 0x400000 +#define TIP_SENSE_UNPLUG 0x200000 +#define BUTTON_DOWN_PRESS 0x10000 +#define BUTTON_UP_PRESS 0x20000 +#define BUTTON_RELEASE 0x100000 +// pressing the play/pause button on earbuds yields 0x100 on down and 0x200 on up +#define BUTTON_TOGGLE_DOWN_PRESS 0x100 +#define BUTTON_TOGGLE_UP_PRESS 0x200 +#define BUTTON_DETECT_MAIN 0x1800 // we only see 0x800 but the mask allows for these 2 bits +#define BUTTON_DETECT_MASK 0x60 +#define BUTTON_DETECT1 0x40 +#define BUTTON_DETECT2 0x20 +#define MIKE_CONNECT 0x02 +#define BUTTONS (BUTTON_UP_PRESS | BUTTON_DOWN_PRESS | BUTTON_TOGGLE_UP_PRESS | BUTTON_TOGGLE_DOWN_PRESS) +#define HSDET_AUTO_DONE 0x02 +#define PDN_DONE 0x01 + +static void cs_8409_headset_plugin_event(struct hda_codec *codec); +static void cs_8409_headset_unplug_event(struct hda_codec *codec); +static void cs_8409_headset_type_detect_event(struct hda_codec *codec); +static int cs_8409_set_power_state(struct hda_codec *codec, int power_state); +static void cs_8409_enable_headset_streaming(struct hda_codec *codec); +static void cs_8409_headset_button_detect_event(struct hda_codec *codec); +static void cs_8409_headset_button_event(struct hda_codec *codec, int buttons); +static void cs_8409_plugin_event_continued(struct hda_codec *codec); + + +static void cs_8409_interrupt_action(struct hda_codec *codec, int int_response) +{ + int retval; + int headset_state; + int update_jacks = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // so if Im analyzing the Dell code correctly + // I think we should only do the snd_hda_jack_report_sync after all jack detection + // plus all nid mute/unmute and widget output/input enable + // because the Dell code first does tip sense, then (if wanted) jack type detection + // then runs snd_hda_jack_unsol_event which does any callbacks + // and finally the snd_hda_jack_report_sync + + if ((int_response & TIP_SENSE_PLUG) == TIP_SENSE_PLUG) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - plug in\n"); + cs_8409_headset_plugin_event(codec); + } + else if ((int_response & TIP_SENSE_UNPLUG) == TIP_SENSE_UNPLUG) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug\n"); + cs_8409_cs42l83_mark_jack(codec); + cs_8409_headset_unplug_event(codec); + // so although this more consistent with linux way (all automute etc callbacks done before report sync) + // it seems we need to update the linux user side before doing the amp reset when playing + // with cs_8409_cs42l83_jack_report_sync here there is a few 10s milliseconds period where get anomalous volume + // because we start playing through the amps while linux user side still says its headphone output + // - this may be because updating the linux user side takes a little while + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + } + else if ((int_response & HSDET_AUTO_DONE) == HSDET_AUTO_DONE) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - headset detected\n"); + + cs_8409_headset_type_detect_event(codec); + + // and this is where life gets really complicated + // if we have a mike we do a button detect - but that leads to an unsolicited response + // so we only continue here I think if we dont have a mike + if (!(spec->have_mike)) { + cs_8409_cs42l83_mark_jack(codec); + cs_8409_plugin_event_continued(codec); + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - headset detect no mike jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + } + } + // not clear what test is here - but this should check what we see - one button interrupt seems to be activated + // when doing the button detect + // not sure what the exact button interrupt is - we get 0x140800 + // so the button detect interrupt is 0x0800 - the 0x140000 are actual button interrupts (undocumented for cs42l42) + else if (int_response & BUTTON_DETECT_MAIN) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - buttons detected\n"); + cs_8409_headset_button_detect_event(codec); + + cs_8409_cs42l83_mark_jack(codec); + + cs_8409_plugin_event_continued(codec); + + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button detect jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + + } + else if (((int_response & BUTTON_UP_PRESS) == BUTTON_UP_PRESS) || + ((int_response & BUTTON_DOWN_PRESS) == BUTTON_DOWN_PRESS) || + ((int_response & BUTTON_TOGGLE_UP_PRESS) == BUTTON_TOGGLE_UP_PRESS) || + ((int_response & BUTTON_TOGGLE_DOWN_PRESS) == BUTTON_TOGGLE_DOWN_PRESS)) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button event on \n"); + cs_8409_headset_button_event(codec, int_response); + update_jacks = 1; + + } + else if (((int_response & BUTTON_RELEASE) == BUTTON_RELEASE)) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button event off \n"); + //cs_8409_cs42l83_mark_jack(codec); + cs_8409_headset_button_event(codec, int_response); + update_jacks = 1; + + } + else if ((int_response & PDN_DONE) == PDN_DONE) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - power down\n"); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - UNKNOWN INTERRUPT 0x%08x\n", int_response); + } + + return; +} + + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec); +static void cs_8409_headset_streaming_preplay(struct hda_codec *codec); +static void cs_8409_intmike_linein_disable(struct hda_codec *codec); +static void cs_8409_headset_mike_streaming_preplay(struct hda_codec *codec, int nullformat); +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec); + + +static void cs_8409_plugin_event_continued(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + int headset_plugged_in = 0; + int retval = 0; + + mycodec_info(codec, "cs_8409_plugin_event_continued start\n"); + + // 0 is power state - 0 is powered on +ve powered/powering down + // headset_plugged_in indicates if headset still plugged in or not + headset_plugged_in = cs_8409_set_power_state(codec, 0); + + mycodec_info(codec, "cs_8409_plugin_event_continued headset_plugged_in %d\n", headset_plugged_in); + + if (headset_plugged_in) + { + // now handle plugin while playing + if (spec->playing) + { + + cs_8409_amps_disable_streaming(codec); + + + cs_8409_enable_headset_streaming(codec); + + + // the following is just cs_8409_enable_headset_streaming + //cs43l83_headset_amp_format_setup(codec); + //mycodec_info(codec, "cs42l83_headset_play_setup_on\n"); + //cs42l83_headset_play_setup_on(codec); + // power on audio output + //cs42l83_set_power_state_on(codec, 0); + //cs42l83_headset_enable_on(codec); + + + // so OSX now does another one of its enable off/enable on - ignoring + + //cs_8409_headset_enable_off(codec); + //cs42l83_power_off_codec_output_off(codec); + //headphone_play_stop_buffers_off(codec); + //headphone_play_stop_power_off(codec); + //headphone_play_stop_headphone_format_setup_disable(codec); + + //cs_8409_enable_headset_streaming(codec); + + // we need to reset formats here - so we follow the same path as a simple amp + // or headphone play ie after the pre-prepare we force a reset of the + // of the stream format + + // using explicit nid here!! + //cs_8409_reset_stream_format(codec, 0xa, 1, 1); + + } + else + { + + // try removing this - we still do a partial setup when actually play on OSX + // and if we stop play then do another play we do a full setup + // - why not just enable when we play?? + // in any case we initially made this a full setup and it worked + //cs_8409_headset_streaming_preplay(codec); + + // so now think on OSX we pre-setup the headphone and mike here + // when we dont know if we will be playing or capturing + // in particular realised that cs42l83_configure_serial_port is only called + // for the headphone setup - but it sets both primary ASP (Audio Serial Port) + // transmit and receive frequencies - which would seem to be important for + // capturing!! + + //spec->headset_presetup_done = 1; + + } + + // NOTA BENE - no concept/implementation of plugging in while capturing!! + + mycodec_info(codec, "cs_8409_plugin_event_continued headset_phase %d\n", spec->headset_phase); + + // this event now gets called if boot with headset plugged in + // but from this point the boot phase setup is different + // - now a headset_phase of 1 indicates booted with headset plugged in + // - headset phase of 2 or more means post boot headset plugin + if (spec->headset_phase >= 2) + { + + // ensure the intmike/linein nids are powered off + cs_8409_inputs_power_nids_off(codec); + + + retval = cs42l83_headphone_sense(codec); + + mycodec_info(codec, "cs_8409_plugin_event_continued headphone sense 0x%x\n", retval); + + if (!(retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_plugin_event_continued JACK DISCONNECT UNIMPLEMENTED!!\n"); + } + + if (spec->have_mike) + { + if (spec->capturing) + { + dev_info(hda_codec_dev(codec), "cs_8409_plugin_event_continued PLUGIN WHILE CAPTURING UNIMPLEMENTED!!\n"); + } + + // this is just calling this routine + //cs_8409_headset_mike_setup_nouse + + cs_8409_intmike_linein_disable(codec); + + // is this a good position to switch the inputs?? + switch_input_src(codec); + + // confirmed that if do a second recording we get a full setup as for playing above + // - so why not just enable when we capture?? + // (only plausible reason so far is to reduce setup time because of the long time + // to send the i2c commands??) + // NOTE - this is complicated because on OSX it appears the headphone setup is always + // done - even if just capturing + // going with OSX way and doing the headphone setup as well + + // so this is a problem - at this point we dont have a stream + // so our format is null + // what to do?? + // now moving all this setup into the actual capture setup + // - as did with the play setup + + //cs_8409_headset_mike_streaming_preplay(codec, 1); + + //spec->headset_presetup_done = 1; + + + cs_8409_headset_mike_buttons_enable(codec); + + } + } + + } + else + {} // headset unplugged - should be handled by the unplug interrupt + + mycodec_info(codec, "cs_8409_plugin_event_continued end\n"); +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec); +static void cs_8409_plugin_complete_detect(struct hda_codec *codec); + + +static void cs_8409_headset_plugin_event(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_plugin_event pre plugin handle_detect\n"); + + cs_8409_plugin_handle_detect(codec); + + mycodec_info(codec, "cs_8409_headset_plugin_event post plugin handle_detect\n"); + + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 1066); // 0x001f0500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // this seems to be here but no idea where coming from + retval = read_gpio_status_check(codec); + + + // following code likely from AppleHDAMikeyInternalCS8409::handleJackDetectUR + // moved from cs_8409_plugin_handle_detect + + // then call setTimer to initiate function after a time period + + // this is NOT a debug sleep - it occurs on all plugin events on OSX for some reason + msleep(1800); + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 1069); // 0x001f0500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + mycodec_info(codec, "cs_8409_headset_plugin_event pre plugin complete_detect\n"); + + cs_8409_plugin_complete_detect(codec); + + mycodec_info(codec, "cs_8409_headset_plugin_event post plugin complete_detect\n"); + + spec->headset_phase = 3; + +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // now think this is AppleHDAMikeyInternalCS8409::handleJackDetectUR + + // which calls: + // AppleHDAMikeyInternalCS8409::disableButtonDetection + // AppleHDAMikeyInternalCS8409::enableHPClamps + + // AppleHDAMikeyInternalCS8409::enableHPClamps calls: + // AppleHDATDM_Codec::setHPOutClamp + + mycodec_info(codec, "cs_8409_plugin_handle_detect start\n"); + + // this is a pre-value - indicates we have had a jack detect + // but set to 1 when have checked with cs42l83_headphone_sense for headset + // at the moment not used + spec->jack_present = 2; + + cs42l83_headset_button_detect_interrupts_off(codec); + + cs42l83_headset_set_hpout_clamp_disable(codec); + + // IOSleep(1) here + msleep(1); + + mycodec_info(codec, "cs_8409_plugin_handle_detect end\n"); +} + +static void cs_8409_plugin_complete_detect(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // so AppleHDAMikeyInternalCS8409::generalTimerCallback calls AppleHDAMikeyInternalCS8409::completeJackDetectUR + // AppleHDAMikeyInternalCS8409::generalTimerCallback is set as a timer callback in the AppleHDAMikeyInternal::init + // using the IOTimerEventSource::timerEventSource function + + // this is AppleHDAMikeyInternalCS8409::completeJackDetectUR + // its first call is to AppleHDATDM_Codec::getHeadphonePinSense + // which if returns 0 in the bool arg jumps to AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // - which does the disconnect + + mycodec_info(codec, "cs_8409_plugin_complete_detect start\n"); + + // so thats weird - the first call is a power call which doesnt seem to exist in the log + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = cs42l83_headphone_sense(codec); + + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone sense 0x%x\n", retval); + + if ((retval & 0x80)) + { + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone sensed\n"); + + spec->jack_present = 1; + spec->headset_enable = 1; + + cs42l83_complete_jack_detect(codec); + + cs42l83_power_hs_bias_on(codec); + + // this seems to be setting an interrupt on 0x131b for headset detect + // - but there doesnt seem to be a delay anywhere here + // so it must be immediately triggered + + cs42l83_enable_hs_auto_int_on(codec); + + + cs42l83_unplug_interrupt_setup(codec); + + + cs42l83_set_hpout_pulldown_off(codec); + + cs42l83_headset_detect_on(codec); + //cs42l83_headset_detect_onoff(codec, 1); + + } + else + { + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone NOT sensed\n"); + + spec->jack_present = 0; + spec->headset_enable = 0; + + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + dev_info(hda_codec_dev(codec), "cs_8409_plugin_complete_detect no headphone UNIMPLEMENTED!!\n"); + + } + + mycodec_info(codec, "cs_8409_plugin_complete_detect end\n"); +} + + + +static void cs_8409_headset_type_detect_event(struct hda_codec *codec) +{ + int retval; + int retstate; + int flag = 1; + int headset_type = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // this is AppleHDAMikeyInternalCS8409::handleTypeDetectUR + // dont yet see the path to call this - Im guessing from some messaging call + + mycodec_info(codec, "cs_8409_headset_type_detect_event start\n"); + + // I think we get to here if have either an unplug event or headset detect event + // - in both cases we need to turn off the headset detect interrupt + // and unset for headset detect + + //plugin_enable_hs_auto_int_off(codec); + cs42l83_enable_hs_auto_int_off(codec); + + //headset_type = plugin_headset_type(codec); + headset_type = cs42l83_headset_type(codec); + + // we then have options based on the headset type + headset_type = headset_type & 0x3; + + // types are 0x00 (1), 0x01 (2), 0x02 (3) and 0x3 (4) + // type 1 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Mic + // type 2 Pin 1 Left, Pin 2 Right, Pin 3 Mic, Pin 4 Gnd + // type 3 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Gnd + // type 4 Optical!! + + mycodec_info(codec, "cs_8409_headset_type_detect_event headset type %d\n", headset_type); + + if (headset_type == 0x00 && flag == 0) + { + // 0x74df1 + + // if ?? goto 0x74ee2 + + // if ?? goto 0x751e8 + + // 0x74e42 + + //plugin_headset_detect_off(codec); + + // IOSleep(250); + + // possible AppleHDAMikeyInternalCS8409::handleJackDetectUR + + // goto 0x7546d + // IOLog() + // goto 0x75198 + // return + + // else + + // 0x751e8 + + //retval = plugin_headphone_sense1(codec); + + // if dont sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + //if (!(retval & 0x80)) + //{ + // // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // goto 0x74ee2 + //} + + // error!! + } + + if (headset_type == 0x00) + { + // 0x74df1 + // goto 0x74ee2 + spec->headset_type = 1; + spec->have_mike = 1; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset has mike!!\n"); + } + else if (headset_type == 0x01) + { + // 0x74e98 + // insert Mikey event 0xfe + // goto 0x74ee2 + spec->headset_type = 2; + spec->have_mike = 1; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset has mike!!\n"); + } + else if (headset_type == 0x02) + { + // 0x74eb9 + // goto 0x74ee2 + spec->headset_type = 3; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset does not have mike!!\n"); + } + else if (headset_type == 0x03) + { + // this is SPDIF!! + // 0x74ec3 + // insert Mikey event 0xfc + spec->headset_type = 4; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset does not have mike!!\n"); + } + + // 0x74ee2 + + cs42l83_headset_detect_off(codec); + //cs42l83_headset_detect_onoff(codec, 0); + + cs42l83_set_hpout_pulldown_on(codec); + + // IOSleep(1); + msleep(1); + + // 0x74ff1 + + cs42l83_set_hpout_clamp_enable(codec); + + // IOSleep(1); + msleep(1); + + + if (spec->have_mike) + { + + cs42l83_enable_hsbias_auto_clamp_on(codec); + + //cs42l83_enable_hsbias_auto_clamp_off(codec); + cs42l83_enable_hsbias_auto_clamp_off0(codec); + + // I dont see a difference in these 2 functions + //cs42l83_power_hs_bias_off1(codec); + cs42l83_power_hs_bias_off(codec); + + // difference from no mike headphones + + cs42l83_setup_button_detect(codec); + + cs42l83_power_hs_bias_button_on(codec); + + cs42l83_enable_hsbias_auto_clamp_off1(codec); + + } + else + { + // goto 0x75a02 + + // 0x750a2 + + cs42l83_headset_mike_detect_off(codec); + + cs42l83_power_hs_bias_off(codec); + } + + + // 0x75149 + + // there is a call to dispatchStatelessTagToEngines which is likely what initiates the stream setup etc + + + // 0x7546d + + // 0x75198 + + // there is an unknown call here - possible setPowerState + // cannot figure out if this is doing anything - none of the functions seem to fit the log + // so now think this function isnt really doing anything + // and the setPowerState is from some other function call path ie the result + // of dispatchStatelessTagToEngines + + + // exit routine after this + + mycodec_info(codec, "cs_8409_headset_type_detect_event end\n"); +} + + +static int cs_8409_set_power_state(struct hda_codec *codec, int power_state) +{ + int retval = 0; + int retstate = 0; + int flag = 0; + + mycodec_info(codec, "cs_8409_set_power_state start\n"); + + // this is likely AppleHDAMikeyInternalCS8409::setPowerState as there is + // a pin sense and handleJackDisconnectUR in AppleHDAMikeyInternalCS8409::setPowerState + // in fact all AppleHDAMikeyInternalCS8409::setPowerState does is check the headphone sense + // and then either do a handleJackDisconnectUR or handleJackDetectUR + + if (flag) + { + // check for headphone whatever power state of HDA is + + retval = cs42l83_headphone_sense(codec); + + // if sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDetectUR + + if ((retval & 0x80)) + { + // AppleHDAMikeyInternalCS8409::handleJackDetectUR + dev_info(hda_codec_dev(codec), "cs_8409_set_power_state JACK DETECT UNIMPLEMENTED!!\n"); + } + + retstate = 0; + } + else + { + // only check for headphone if HDA powered up + if (power_state == 0) + { + retval = cs42l83_headphone_sense(codec); + + // if dont sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + if (!(retval & 0x80)) + { + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + dev_info(hda_codec_dev(codec), "cs_8409_set_power_state JACK DISCONNECT UNIMPLEMENTED!!\n"); + retstate = 0; + } + else + retstate = 1; + } + } + + mycodec_info(codec, "cs_8409_set_power_state end\n"); + + return retstate; +} + + +static void cs_8409_headset_button_detect_event(struct hda_codec *codec) +{ + int ret_button; + int ret_mike; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_button_detect_event start\n"); + + // this returns significant state - headphone sense (shift 16), and 2 reads from register 0x1b78 (second one shifted 8) + ret_button = cs42l83_handle_button_detect(codec); + + mycodec_info(codec, "cs_8409_headset_button_detect_event button data 0x%08x\n", ret_button); + + // so now seen on imacs we have a button detect of 0x20 rather than 0x40 previously seen + // - this maybe an Apple headset/non-Apple headset issue rather than imac issue (the headset was non-Apple) + //if ((ret_button & BUTTON_DETECT) == BUTTON_DETECT) + if (ret_button & BUTTON_DETECT_MASK) + { + mycodec_info(codec, "cs_8409_headset_button_detect_event HAVE BUTTON\n"); + spec->have_buttons = 1; + } + + // this is a read from same register 0x1b78 - which seems to contain both senses + // - button sense 0x40/0x20 (assumed) and mike sense 0x02 - known but undocumented + // do we do anything with this?? + // we have aleady set have_mike prior to this + // could log an error here + ret_mike = cs42l83_mike_connected(codec); + + mycodec_info(codec, "cs_8409_headset_button_detect_event mike data 0x%08x\n", ret_mike); + + if ((ret_mike & MIKE_CONNECT) != MIKE_CONNECT) + dev_err(hda_codec_dev(codec), "ERROR - has mike but mike not connected - not analyzed!!\n"); + + + mycodec_info(codec, "cs_8409_headset_button_detect_event end\n"); +} + +static void cs_8409_headset_button_event(struct hda_codec *codec, int buttons) +{ + mycodec_info(codec, "cs_8409_headset_button_event buttons 0x%08x\n", buttons); +} + + +static void cs_8409_intmike_linein_disable(struct hda_codec *codec) +{ + + cs_8409_intmike_stream_conn_off(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_format_setup_disable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + +} + +static void cs_8409_headset_mike_streaming_preplay(struct hda_codec *codec, int nullformat) +{ + + // confirmed that if do a second recording we get a full setup as for playing + // - so why not just enable when we capture?? + // (only plausible reason so far is to reduce setup time because of the long time + // to send the i2c commands??) + // NOTE - this is complicated because on OSX it appears the headphone setup is always + // done - even if just capturing + // going with OSX way and doing the headphone setup as well + + //cs42l83_headset_mike_format_setup_enable(codec, nullformat, 1); + + //cs42l83_input_set_output_sample_rate(codec); + + //cs42l83_mike_setup_audio_input(codec); + + //cs42l83_mike_enable(codec); + + + // yet again the usual disable/second enable + + //cs42l83_mike_disable(codec); + + //cs42l83_headset_mike_format_setup_disable(codec); + + + // cs42l83_headset_mike_format_setup_enable1(codec); + + // cs42l83_headset_mike_format_setup_enable1a(codec); + + // cs42l83_headset_mike_format_setup_enable1b(codec); + + // cs42l83_input_set_output_sample_rate1(codec); + + // cs42l83_input_setup_audio_input(codec); + + // cs42l83_mike_enable(codec); + + + //cs42l83_headset_mike_pin_enable(codec); + +} + +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec) +{ + + // part of AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + cs42l83_configure_headset_button_interrupts(codec); + + cs42l83_enable_hsbias_auto_clamp_off2(codec); + + // following coded explicitly in handleButtonDetectUR + + cs42l83_hsbias_sense_on(codec); + +} + +static void cs_8409_headset_plugin_preplay_unused1(struct hda_codec *codec) +{ + + // these occur for headset with mike but seem to be triggered by + // by something - maybe volume control?? + // dont know where coming from - ignoring for now + + //plugin3_set_power_state_on(codec); + + //plugin3_headphone_play_start_headset_enable_on(codec); + + + //plugin3_headphone_sense3(codec); + + //plugin3_headphone_sense4(codec); + + + //plugin3_headphone_format_setup_enable2(codec); + +} + +static void cs_8409_headset_plugin_preplay_unused2(struct hda_codec *codec) +{ + // these occur for headset with mike but seem to be triggered by + // by something - this is definitely after an interrupt - but + // no interrupt triggered + // dont know where coming from - ignoring for now + + //plugin3_headset_enable_off(codec); + + //plugin3_power_off_codec_output_off(codec); + + //plugin3_buffers_off1(codec); + + //plugin3_headphone_format_setup_disable1(codec); + + //plugin3_headset_enable_off1(codec); + + //plugin3_power_off_codec_output_off1(codec); + +} + + +static void cs_8409_unplug_handle_disconnect(struct hda_codec *codec); + +static void cs_8409_headset_unplug_event(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_unplug_event start\n"); + + // Im guessing we are ensuring headphone is unplugged here + // what to do if not!! + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_headset_unplug_event headphone still sensed - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_headset_unplug_event headphone not sensed - OK\n"); + + cs_8409_unplug_handle_disconnect(codec); + } + + mycodec_info(codec, "cs_8409_headset_unplug_event end\n"); +} + +static void cs_8409_unplug_handle_disconnect(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect start\n"); + + + cs42l83_plugin_interrupt_setup(codec); + + cs42l83_enable_hs_auto_int_off(codec); + + cs42l83_headset_detect2_off(codec); + + + if (spec->have_mike) + { + cs42l83_power_hs_bias_off(codec); + + cs42l83_enable_hsbias_auto_clamp_off3(codec); + + cs42l83_disable_button_interrupts(codec); + } + + + // Im guessing we are ensuring headphone is unplugged here + // what to do if not!! + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_err(hda_codec_dev(codec), "cs_8409_unplug_handle_disconnect headphone still sensed - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headphone not sensed - OK\n"); + + // even here this still has audio glitch + // - but with 100 ms wait later seems to fix it + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + spec->jack_present = 0; + + // try setting ALL jacks dirty - likely not needed + //snd_hda_jack_set_dirty_all(codec); + + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + + + if (spec->playing) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 1 start\n"); + + cs42l83_headset_enable_off(codec); + + cs42l83_power_off_codec_output(codec); + + //unplug23_buffers_off(codec); + cs42l83_buffers_onoff(codec, 0); + + if ((spec->have_mike)) + cs_8409_headset_amp_format_setup_disable(codec, 0); + else + { + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + } + + + cs42l83_headset_enable_off(codec); + + //unplug23_power_off_codec_output1(codec); + cs42l83_power_off_codec_output(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 1 end\n"); + } + + // so we have determined the volume/glitch issues are after this + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 1\n"); + //msleep(10000); + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 1 end\n"); + + // with previous jack_report_sync and this wait dont have a glitch + msleep(100); + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + //spec->jack_present = 0; + + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + + + // add a wait for user side update - still get a small glitch + //msleep(100); + + + // this is done if playing or not?? + // - changing - only setup amps if still playing + + if (spec->playing) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 2 start\n"); + + //unplug23_play_setup_TDM_6462(struct hda_codec *codec) + + //play_setup_TDM_amps12_debug(codec, 1); + play_setup_TDM_amps12(codec, 1); + + //unplug23_setup_amps_6462(struct hda_codec *codec) + + play_setup_amps12(codec); + + //unplug23_play_setup_TDM_7472(struct hda_codec *codec) + + play_setup_TDM_amps34(codec); + + //unplug23_play_setup_amps_7472(struct hda_codec *codec) + + play_setup_amps34(codec); + + //unplug23_sync_converters_on(struct hda_codec *codec) + + play_sync_converters_on(codec); + + + // so here linux user side reports still headphone output (because originally had not yet done + // jack_report_sync) which leads to audio glitch with output now through speakers + // so we need to update linux user side after headphone output disable above + // (the volume mismatch previously heard was due to incorrect handling of nid 0x03 update for stereo (ie 2 channel) source) + // can only think its delay from jack_report_sync till linux user side updated + // (we dont really have a massive lot of commands from here till jack_report_sync + // (here would be more consistent with linux way which does all power/automute/automic etc callbacks before jack_report_sync) + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 2\n"); + //msleep(10000); + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 2 end\n"); + + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + //spec->jack_present = 0; + + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 2 end\n"); + } + + // more duplicated disable/enables + + //unplug_sync_converters_off(codec); + //unplug_disable_amps_6462(codec); + //unplug_disable_TDM_6462(codec); + //unplug_disable_amps_7472(codec); + //unplug_disable_TDM_7472(codec); + + //unplug_setup_TDM2_6462(codec); + //unplug_setup_amps1_6462(codec); + //unplug_setup_TDM2_7472(codec); + //unplug_setup_amps1_7472(codec); + //unplug_sync_converters2_on(codec); + + + if (!(spec->have_mike)) + { + cs_8409_inputs_power_nids_off(codec); + } + + // and another headphone sense + + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_err(hda_codec_dev(codec), "cs_8409_unplug_handle_disconnect headphone sensed again - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset not sensed - OK\n"); + + + if (spec->have_mike) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset have mike start\n"); + + //unplug23_mike_disable(codec); + + cs42l83_mike_disable(codec); + + //unplug23_power_on_off(codec); + + cs42l83_power_onoff(codec, 0); + + //unplug23_headset_amp_disable_and_mike_format_setup_disable(codec); + + cs42l83_headset_amp_disable_and_mike_format_setup_disable(codec); + + // is this a good position to switch the inputs?? + switch_input_src(codec); + + //unplug23_intmike_linein_resetup(codec); + + cs_8409_intmike_linein_resetup(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset have mike end\n"); + } + + if (spec->playing) + { + if (!(spec->have_mike)) + { + cs_8409_inputs_power_nids_off(codec); + } + } + + if (spec->have_mike) + cs_8409_inputs_power_nids_off(codec); + + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable start\n"); + + cs42l83_unplug_headset_detect_off(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable 1\n"); + + cs42l83_headset_switch_control(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable end\n"); + } + + } + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset about to reset vars\n"); + + // and reset all headset variables + + spec->jack_present = 0; + + spec->headset_type = 0; + + spec->have_mike = 0; + + spec->have_buttons = 0; + + spec->headset_play_format_setup_needed = 1; + spec->headset_capture_format_setup_needed = 1; + + spec->headset_presetup_done = 0; + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect jack_present %d\n", spec->jack_present); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect end\n"); +} + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec) +{ + + // this is the function AppleHDATDM_CS42L83::enable for turning on headset for play + + + // the following section is often done pre-play + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + // the following section always done before play + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + +} + + +static void cs42l83_headset_disable_nouse(struct hda_codec *codec, int full) +{ + myprintk("snd_hda_intel: cs42l83_headset_disable start\n"); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs42l83_power_off_codec_output end\n"); + + if (full) + { +#if 0 + // so this is old style - why didnt I update it?? + myprintk("snd_hda_intel: cs42l83_headset_rcv_enable_off start\n"); + + cs42l83_headset_rcv_enable_off(codec); + + myprintk("snd_hda_intel: cs42l83_headset_rcv_enable_off end\n"); + myprintk("snd_hda_intel: cs42l83_headset_power_off start\n"); + + cs42l83_headset_power_off(codec); + + myprintk("snd_hda_intel: cs42l83_headset_power_off end\n"); +#endif + myprintk("snd_hda_intel: cs42l83_buffers_onoff start\n"); + + //cs42l83_headset_rcv_enable_off(codec); + cs42l83_buffers_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff end\n"); + myprintk("snd_hda_intel: cs42l83_power_onoff start\n"); + + //cs42l83_headset_power_off(codec); + cs42l83_power_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_power_onoff end\n"); + } + + myprintk("snd_hda_intel: cs42l83_headset_disable end\n"); +} + +static void cs42l83_headset_disable(struct hda_codec *codec) +{ + myprintk("snd_hda_intel: cs42l83_headset_disable start\n"); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff start\n"); + + //cs42l83_headset_rcv_enable_off(codec); + cs42l83_buffers_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff end\n"); + myprintk("snd_hda_intel: cs42l83_power_onoff start\n"); + + //cs42l83_headset_power_off(codec); + cs42l83_power_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_power_onoff end\n"); + + myprintk("snd_hda_intel: cs42l83_headset_disable end\n"); +} + + +static void cs_8409_headset_streaming_preplay(struct hda_codec *codec) +{ + //int retval; + + // this code performed on plugin even if not playing + // the usual enable/disable multiple times + + //plugin3_headphone_format_setup_enable(codec); + cs43l83_headset_amp_format_setup(codec, 1, 1); + + //plugin3_configure_int_mclk(codec); + cs42l83_configure_int_mclk(codec); + + //plugin3_power_on_on(codec); + cs42l83_power_onoff(codec, 1); + + //plugin3_configure_serial_port(codec); + cs42l83_configure_serial_port(codec); + + //plugin3_set_sample_rate(codec); + cs42l83_output_set_input_sample_rate(codec); + + //plugin3_setup_audio_output(codec); + cs42l83_setup_audio_output(codec); + + //plugin3_buffers_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + // ignoring the disable + + //plugin3_buffers_off(codec); + //cs42l83_buffers_onoff(codec, 0); + + //plugin3_power_on_off(codec); + //cs42l83_headset_power_off(codec); + + //plugin3_headphone_format_setup_disable(codec); + //cs_8409_headset_amp_format_setup_disable(codec, 1); + + + //plugin3_headphone_format_setup_enable1(codec); + + //plugin3_headphone_format_setup_enable1a(codec); + + //plugin3_configure_int_mclk1(codec); + + //plugin3_power_on_on2(codec); + + //plugin3_configure_serial_port1(codec); + + //plugin3_set_sample_rate1(codec); + + //plugin3_setup_audio_output1(codec); + + //plugin3_buffers_on1(codec); + +} + + +static void cs_8409_enable_headset_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_enable_headset_streaming start\n"); + + // debug status check - 0x27 here + read_gpio_status_check(codec); + + // dont really have any idea how to get to here + // Im guessing from messaging + + //play_format_setup_headphone(codec); + cs43l83_headset_amp_format_setup(codec, 1, 1); + + cs42l83_headset_play_setup_on(codec); + + // power on audio output + cs42l83_set_power_state_on(codec, 0); + + cs42l83_headset_enable_on(codec); + + mycodec_info(codec, "cs_8409_enable_headset_streaming end\n"); + +} + +static void cs_8409_disable_headset_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_streaming start\n"); + + // why do we do the headphone disable/poweroff codec output twice?? + // but we do - repeatedly seen in logs + + // this was cs42l83_headset_disable(codec, 1); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming cs42l83_power_off_codec_output end\n"); + + cs42l83_headset_disable(codec); + + + //playstop_headset_amp_format_setup_disable_headphone(codec); + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // this was cs42l83_headset_disable(codec, 0); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming 1 cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming 1 cs42l83_power_off_codec_output end\n"); + + + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_streaming end\n"); +} + + +static void cs_8409_headplay_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headplay_real start\n"); + + if (spec->headset_enable == 0) + { + mycodec_info(codec, "cs_8409_headplay_real enable 0\n"); + //cs_8409_headplay_real1(codec); + } + else if (spec->headset_enable == 1) + { + mycodec_info(codec, "cs_8409_headplay_real enable 1\n"); + //cs_8409_headplay_real1(codec); + cs_8409_enable_headset_streaming(codec); + //spec->headset_enable = 2; + } + else if (spec->headset_enable == 2) + { + mycodec_info(codec, "cs_8409_headplay_real enable 2\n"); + //cs_8409_headplay_real2(codec); + } + + mycodec_info(codec, "cs_8409_headplay_real end\n"); +} + + +static void cs_8409_headplaystop_real(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_headplaystop_real start\n"); + + cs_8409_disable_headset_streaming(codec); + + mycodec_info(codec, "cs_8409_headplaystop_real end\n"); +} + +static void cs_8409_enable_headset_mike_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_enable_headset_mike_streaming start\n"); + + // debug status check - 0x27 here + read_gpio_status_check(codec); + + // dont really have any idea how to get to here + // Im guessing from messaging + + + // NOTE - there are big ordering issues here + // - here we setup the headphone output before the mike + // - this maybe because Quicktime defaults to enabling play when recording + // unfortunately looks as tho linux tends to open the capture stream before the playback stream + // - so going to ignore this here + + // this sets up the headphones + // note this only does a partial headset amp setup compared to a base headset play + // power on audio output + //cs42l83_set_power_state_on(codec, 0); + //cs42l83_headset_enable_on(codec); + + //retval = cs42l83_headphone_sense(codec); + //mycodec_info(codec, "cs_8409_enable_headset_mike_streaming headphone sense 0x%x\n", retval); + //if (!(retval & 0x80)) + //{ + // dev_info(hda_codec_dev(codec), "cs_8409_enable_headset_mike_streaming headphone NOT PLUGGED IN UNIMPLEMENTED!!\n"); + //} + //retval = cs42l83_headphone_sense(codec); + //mycodec_info(codec, "cs_8409_enable_headset_mike_streaming headphone sense 0x%x\n", retval); + //if (!(retval & 0x80)) + //{ + // dev_info(hda_codec_dev(codec), "cs_8409_enable_headset_mike_streaming headphone NOT PLUGGED IN UNIMPLEMENTED!!\n"); + //} + + ////cs43l83_headset_amp_format_setup_partial + //cs43l83_headset_amp_format_setup(codec, 0); + + + cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + cs42l83_input_set_output_sample_rate(codec); + + cs42l83_mike_setup_audio_input(codec); + + cs42l83_mike_enable(codec); + + + // power on the codec/audio input + cs42l83_set_power_state_on(codec, 1); + + // unmute audio input + cs42l83_headset_mike_adc_unmutevol(codec, 1); + + + // for partial setup only + //cs42l83_headset_mike_pin_enable(codec); + //cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + + // this is all done in the capture hook after this call + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + mycodec_info(codec, "cs_8409_enable_headset_mike_streaming end\n"); +} + +static void cs_8409_disable_headset_mike_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_mike_streaming start\n"); + + + // NOTE - there are big ordering issues here + // although here the mike is turned off before the headphone output + + // mute ADC + cs42l83_headset_mike_adc_unmutevol(codec, 0); + + cs42l83_power_off_codec_input(codec); + + cs42l83_mike_disable(codec); + + cs42l83_headset_mike_format_setup_disable(codec); + + // and duplicate the above!! + cs42l83_headset_mike_adc_unmutevol(codec, 0); + cs42l83_power_off_codec_input(codec); + + + // the following is disabling the headphone component + // - assuming this is done by the playback hooks + + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + //cs42l83_buffers_onoff + //cs42l83_headset_power_off + //cs_8409_headset_amp_disable_TDM_proper (full) + //cs_8409_headset_amp_format_setup_disable + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + + // and re-enabling the headphone component????? + // igoring all the following for the moment + + // note there was a 7 second delay here - this is recextstop2/2c + + //cs43l83_headset_amp_format_setup (full) + //cs42l83_configure_int_mclk + //cs42l83_power_onoff + //cs42l83_configure_serial_port + //cs42l83_output_set_input_sample_rate + //cs42l83_setup_audio_output + //cs42l83_buffers_onoff + //cs42l83_set_power_state_on + //cs42l83_headset_enable_on + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + //cs_8409_external_device_unsolicited_response (continued) + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + + // then re-disabling!! + + // note there was a 5 second delay here - this is recextstop3 + + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + //cs42l83_headset_rcv_enable_off + //cs42l83_headset_power_off + //cs_8409_headset_amp_disable_TDM_proper - full + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_mike_streaming end\n"); +} + +static void cs_8409_headcapture_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headcapture_real start\n"); + + if (spec->headset_enable == 0) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 0\n"); + //cs_8409_headcapture_real1(codec); + } + else if (spec->headset_enable == 1) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 1\n"); + //cs_8409_headcapture_real1(codec); + hda_check_power_state(codec, 0x1a, 11); + hda_check_power_state(codec, 0x3c, 11); + cs_8409_enable_headset_mike_streaming(codec); + hda_check_power_state(codec, 0x1a, 12); + hda_check_power_state(codec, 0x3c, 12); + //spec->headset_enable = 2; + } + else if (spec->headset_enable == 2) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 2\n"); + //cs_8409_headcapture_real2(codec); + } + + mycodec_info(codec, "cs_8409_headcapture_real end\n"); +} + + +static void cs_8409_headcapturestop_real(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_headcapturestop_real start\n"); + + cs_8409_disable_headset_mike_streaming(codec); + + mycodec_info(codec, "cs_8409_headcapturestop_real end\n"); +} + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h new file mode 100644 index 0000000..c31b1c1 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h @@ -0,0 +1,1955 @@ + +// this contains functions converted to use proper i2c calls + +// re-define this here so can turn this off for this file specifically +#define mycodec_i2c_local_info mycodec_i2c_info + +static void cs42l83_headset_button_detect_interrupts_off(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_button_detect_interrupts_off\n"); + + // this could be AppleHDAMikeyInternalCS8409::disableButtonDetection + // - this calls 0x1b7c and 0x1b7a + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // register 0x1b7c - this is undocumented for 42l42 (reserved) - maybe more interrupt mask registers + // sort of assuming it shows which interrupts have occurred + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff (default) to 0xff (all masked) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + +} + +static void cs42l83_headset_set_hpout_clamp_disable(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_set_hpout_clamp_disable\n"); + + // AppleHDATDM_CS42L83::setHPOutClamp + + // register 0x1f06 - DAC Control 2 + // changed from 0x2 (default) to 0x06 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0602 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + +} + +static void cs42l83_complete_jack_detect(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_complete_jack_detect\n"); + + // in AppleHDAMikeyInternalCS8409::completeJackDetectUR + + // similar calls also appear in AppleHDATDM_CS42L83::enterStandby - but these are explicit calls in completeJackDetectUR + + // so this appears to change 0x1b75 from 0x9f to 0xb5 + // according to docs this is 0x80 LATCH_TO_VP set + // and HS_DETECT_LEVEL from 0x1f (default) to 0x35 + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) to 0xb5 (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x35) + + // maybe readMikey with 0x6 1st arg doesnt do anything?? + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x1 (Output HiZ, powered Down) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x759f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x75b5 i2c data 0x00b5 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7401 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x00b5, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0001, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_on\n"); + + // AppleHDAMikeyInternalCS8409::powerHSBIAS + + // readMikey analysis for AppleHDATDM_CS42L83::readMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 8 from it compare in range 0 to 5 + // (so 8 becomes 0, 9 becomes 1, 10 becomes 2, 11 becomes 3) + // 0x111f is loaded by default + // 8 0 is 0x111f + // 9 1 is 0x1120 + // 10 2 is 0x1121 + // 11 3 is 0x1124 + + // writeMikey analysis for AppleHDATDM_CS42L83::writeMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 6 from it compare in range 0 to 7 + // (so 8 becomes 2, 9 becomes 3, 10 becomes 4) + // 0x111f is loaded by default + // 8 2 is 0x111f + // 9 3 is 0x1120 + // 10 4 is 0x1121 + // 11 5 is 0x1124 + + // register 0x1b74 - Miscellaneous Detect Control + // value 0x1 (Output HiZ, powered Down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x0 (default) to 0x8 (manual disabled, hs bias closed, auto time 10 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set to 0x7 (Output 2.7V, powered Down) - headset interface functional + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8 to 0x0 (default) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7401 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2008 i2c data 0x0008 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7407 i2c data 0x0007 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2008 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x0000 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0008, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0007, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0000, 1); // snd_hda + +} + +static void cs42l83_enable_hs_auto_int_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_enable_hs_auto_int_on\n"); + + // AppleHDATDM_CS42L83::enableHSDetAutoInt(bool) + // this involves a read then write + + // register 0x131b - Codec Interrupt Mask + // changed from 0x3 (default) to 0x1 + // this is enabling auto headset detection (well HSDET_AUTO_DONE) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b01 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0001, 1); // snd_hda + +} + +static void cs42l83_unplug_interrupt_setup(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_unplug_interrupt_setup\n"); + + // in AppleHDAMikeyInternalCS8409::completeJackDetectUR + + // interrupt clearing seems to be triggered by reading registers in this routine (hence the name) + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Interrupt + // Detect Interrupt 1 Status + // value 0x00 + // register 0x1b79 - Detect Interrupt Mask 1 + // changed from 0xa0 to 0xc0 + // this masks TIP_SENSE_PLUG and unmasks TIP_SENSE_UNPLUG + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79a0 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x79c0 i2c data 0x00c0 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + newval = (retval & 0x9f) | 0x40; + + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, newval, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, 0x00c0, 1); // snd_hda + +} + +static void cs42l83_set_hpout_pulldown_off(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_off\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown + + // register 0x1f06 - DAC Control 2 + // changed from 0x6 to 0x86 (no HPOUT_PULL_DOWN) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0686 i2c data 0x0086 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0086, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x80, 1); // snd_hda + +} + +static void cs42l83_headset_detect_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_on\n"); + + // this is AppleHDAMikeyInternalCS8409::enableHSDetection + // this calls readMikey/writeMikey with either 0x8 arg or 0x9 arg + // I think 0x8 arg is one used in the log + + // readMikey analysis for AppleHDATDM_CS42L83::readMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 7 from it compare in range 0 to 5 + // (so 8 becomes 1, 9 becomes 2) + // 0x111f is loaded by default + // 1 is 0x1120 + + // writeMikey analysis for AppleHDATDM_CS42L83::writeMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 5 from it compare in range 0 to 7 + // (so 8 becomes 3, 9 becomes 4) + // 0x111f is loaded by default + // 3 is 0x1120 + + // register 0x111f - Headset Detect Control 1 + // changed from 0x77 (default) to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0x00 (default) to 0xc2 + // 0xc0 headset detection automatic, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f77 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x20c2 i2c data 0x00c2 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x00c2, 1); // snd_hda + +} + +static void cs42l83_headset_detect_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_off\n"); + + // AppleHDAMikeyInternalCS8409::enableHSDetection + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0xc2 (default) to 0x82 + // 0x80 headset detection disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x20c2 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_headset_detect_onoff(struct hda_codec *codec, int onstate) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_onoff\n"); + + // AppleHDAMikeyInternalCS8409::enableHSDetection + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0xc2 (default) to 0x82 + // 0x80 headset detection disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x20c2 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + if (onstate) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + if (0xc0 >= retval) + { + newval = (retval & 0x3f) | 0x80; + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + } + } + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + if (onstate) + newval = (retval & 0x3c) | 0x80; + else + newval = retval | 0xc0; + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda +} + +static void cs42l83_enable_hs_auto_int_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hs_auto_int_off\n"); + + // from AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // AppleHDATDM_CS42L83::enableHSDetAutoInt + + // register 0x131b - Codec Interrupt Mask + // changed from 0x3 (default) to 0x3 + // this is disabling auto headset detection (well HSDET_AUTO_DONE) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b03 i2c data 0x0003 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + + newval = (retval & 0xfd) | 0x02; + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0003, 1); // snd_hda + +} + +static int cs42l83_headset_type(struct hda_codec *codec) +{ + int retval_type; + + mycodec_i2c_local_info(codec, "cs42l83_headset_type\n"); + + // this is readMikey function 0xb + + // register 0x1124 - Headset Detect Status + // value 0x06 - 0x4 undocumented, headset type 0x02 (3) + + // types are 0x00 (1), 0x01 (2), 0x02 (3) and 0x3 (4) + // type 1 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Mic + // type 2 Pin 1 Left, Pin 2 Right, Pin 3 Mic, Pin 4 Gnd + // type 3 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Gnd + // type 4 Optical!! + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2400 i2c data 0x2406 + + retval_type = cs_8409_vendor_i2cRead(codec, 0x90, 0x1124, 1); // snd_hda + + return retval_type; +} + +static void cs42l83_set_hpout_pulldown_on(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_on\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown(bool) + + // register 0x1f06 - DAC Control 2 + // changed from 0x86 to 0x06 (HPOUT_PULL_DOWN on) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0686 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + +} + + +static void cs42l83_set_hpout_pulldown_onoff(struct hda_codec *codec, int onstate) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_onoff\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown(bool) + + // register 0x1f06 - DAC Control 2 + // changed from 0x86 to 0x06 (HPOUT_PULL_DOWN on) + + // register 0x1f06 - DAC Control 2 + // changed from 0x6 to 0x86 (no HPOUT_PULL_DOWN) + + if (onstate) + { + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0686 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x00, 1); // snd_hda + } + else + { +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0686 i2c data 0x0086 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0086, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x80, 1); // snd_hda + } + +} + + +static void cs42l83_set_hpout_clamp_enable(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_clamp_enable\n"); + + // AppleHDATDM_CS42L83::setHPOutClamp + + // register 0x1f06 - DAC Control 2 + // changed from 0x06 to 0x02 (default) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0602 i2c data 0x0002 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0x04, 0x00, 1); // snd_hda +} + +static void cs42l83_headset_mike_detect_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_detect_off\n"); + + // in AppleHDAMikeyInternalCS8409::handleTypeDetectUR + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x7 to 0x7 (Output 2.7V, mic detect powered Down) - headset interface functional + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7407 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7407 i2c data 0x0007 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + // this sets the detect mode to 0b00 (default, inactive) + newval = (retval & 0xe7); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0007, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_off\n"); + + // multiple calls to this + // AppleHDAMikeyInternalCS8409::handleTypeDetectUR + // AppleHDAMikeyInternalCS8409::completeJackDetectUR + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::powerHSBIAS(unsigned int) + + // register 0x1b74 - Miscellaneous Detect Control + // value 0x7 (Output 2.7V, mic level detect powered Down) - headset interface functional + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 (automatic disabled, auto time 50 microsecs) to 0x8a (automatic disabled, hs bias closed, auto time 50 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set 0x3 (default) (Output weak ground, mic level detect powered Down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8a to 0x82 ((automatic disabled, auto time 50 microsecs) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7407 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x208a i2c data 0x008a + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x208a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x008a, 1); // snd_hda + + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + + //IOSleep(0x28); + msleep(0x28); + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + + //IOSleep(0x14); + msleep(0x14); +} + + +static void cs42l83_enable_hsbias_auto_clamp_on(struct hda_codec *codec) +{ + + //int updval; + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_on\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 + + // explicit coding + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + // bit coding + //updval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //updval &= 0x3f; + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off(struct hda_codec *codec) +{ + + int updval; + int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + // set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x03 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + // explicit coding + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + + // bit coding + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + updval = retval & 0x3f; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + + updval = (retval & 0xb8) | 0x46; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + + +static void cs42l83_enable_hsbias_auto_clamp_off0(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off0\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + // set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x03 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_setup_button_detect(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_setup_button_detect\n"); + + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xff all masked + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xe7 (this appears to be a difference from the 42l42 doc + // - for the 42l42 0xe7 0x10 and 0x08 are reserved bits - maybe mike button bits) + // register 0x1b76 - Mic Detect Control 2 + // changed from 0x2f (default) to 0x3f (again undocumented bit) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x3 (0x02 hsbias ctl weak ground, 0x01 mic lvl detect powered down) + // register 0x1b78 - Detect Status 2 + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x1b (0x18 normal mode, 0x2 weak ground, 0x01 mic lvl detect powered down) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x1b to 0x9b (0x80 undocumented) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7ae7 i2c data 0x00e7 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7600 i2c data 0x762f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x763f i2c data 0x003f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x78d0 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741b i2c data 0x001b +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x741b +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x749b i2c data 0x009b + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00e7, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b76, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b76, 0x003f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x009b, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_button_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_button_on\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::powerHSBIAS + + // register 0x1b74 - Miscellaneous Detect Control + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + // value 0x9b (0x80 undocumented, 0x18 normal mode, 0x2 weak ground, 0x1 mic DC level detect powered down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 (automatic disabled, auto time 50 microsecs) to 0x8a (automatic disabled, hs bias closed, auto time 50 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set 0x9f (0x80 undocumented, 0x18 normal mode, 0x6 2.7V headset interface functional, 0x1 mic DC level detect powered down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8a to 0x82 ((automatic disabled, auto time 50 microsecs) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x749b +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x208a i2c data 0x008a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x749f i2c data 0x009f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x208a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x009f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off1(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off1\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static int cs42l83_handle_button_detect(struct hda_codec *codec) +{ + int ret_ignore; + int ret_mask; + int ret_sense; + int ret_state1 = 0; + int ret_state2 = 0; + int ret_detect1 = 0; + int ret_detect2 = 0; + + mycodec_i2c_local_info(codec, "cs42l83_handle_button_detect\n"); + + // AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xe7 (0x18 unmasked - undocumented - maybe mike buttons - rest masked) + // register 0x1b7c - this is undocumented for 42l42 (reserved) + // Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xe7 state of 0x1b7a) + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xe7 to 0xff + // register 0x1b77 - Detect Status 1 + // value 0x92 0x80 HP plugged bias 0x12 + // register 0x1b78 - Detect Status 2 + // value 0x40 (undocumented - maybe this indicates we have buttons) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x9f to 0x1f + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7ae7 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7ae7 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7700 i2c data 0x7792 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7840 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7840 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x749f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741f i2c data 0x001f + + ret_mask = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + //if ((ret_mask & 0xe7) != 0xe7) + if ((~ret_mask & 0x18) != 0x18) + { + printk("snd_hda_intel: cs42l83_handle_button_detect invalid mask 0x%08x\n",ret_mask); + return 0; + } + + // dont get this - return is 0 - we have no interrupts signalled in the unmasked section?? + // the cs42l42 documentation mentions shadow registers which says reading this register + // loads the shadow register contents to register 0x1b7c - but dont see such a 2nd read here?? + // maybe we are just clearing any bad initial state?? + ret_ignore = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + // this is headphone sense - not clear what we do if dont sense headphones + ret_sense = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b77, 1); // snd_hda + + ret_state1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + ret_state2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001f, 1); // snd_hda + + // possible coding from assembler - do not rely on!! + //ret_detect1 = (ret_state >> 2) & 0x7; + //ret_detect2 = (ret_state + ret_state) & 0x6; + //ret_state1 = (((ret_state1 >> 0x7) | ret_detect2) >> 4) & 0x7; + + // this seems to be returning 0x40 - which is undocumented + // for the moment going to assume 0x40 means we have buttons + return ((ret_sense & 0xff) << 16) | ((ret_state2 & 0xff) << 8) | (ret_state1 & 0xff); +} + +static int cs42l83_mike_connected(struct hda_codec *codec) +{ + int ret_connect = 0; + int retval = 0; + + mycodec_i2c_local_info(codec, "cs42l83_mike_connected\n"); + + // from AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // from AppleHDAMikeyInternal::configureHeadset(unsigned char, unsigned char, unsigned char) + + // AppleHDAMikeyInternalCS8409::microphoneConnected(bool*) + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x1f to 0x1e + // register 0x1b75 - Mic Detect Control 1 + // changed from 0xb5 to 0xb5 (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x35) + // register 0x1b78 - Detect Status 2 + // value 0x42 (0x40 unknown 0x2 hsbias_in true) + // register 0x1b74 - Miscellaneous Detect Control + // set to 0x1f + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x741f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741e i2c data 0x001e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x75b5 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x75b5 i2c data 0x00b5 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7842 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741f i2c data 0x001f + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x00b5, 1); // snd_hda + + // this is returning a different value - now get 0x42 + // assuming 0x40 indicates we have buttons + // and 0x02 seems to be indication we have a mike (from code analysis) + ret_connect = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + // this converts 0x2 to a bool response ie 0x0 or 0x1 + retval = ((ret_connect > 1) & 0x01); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001f, 1); // snd_hda + + return ret_connect; +} + + +static void cs42l83_configure_int_mclk(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_configure_int_mclk\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_configureIntMClk + + // configuring clocks in the CS42L83 is extremely complex + // multiple divisors/multipliers + // only some registers decoded here + + // register 0x1007 - Serial Port SRC Control + // changed from 0x10 to 0x10 - bypass equalizer + + // register 0x1009 - MCLK Control + // .... + + // register 0x1208 - ASP Frame Configuration + // set to 0x00 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0700 i2c data 0x0710 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0710 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0902 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0c02 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x02ff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x039c i2c data 0x009c +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0470 i2c data 0x0070 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x052e i2c data 0x002e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x1b03 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0a5d i2c data 0x005d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0810 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x05ff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0600 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0800 i2c data 0x0000 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1007, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1007, 0x0010, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1009, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120c, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1502, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1503, 0x009c, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1504, 0x0070, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1505, 0x002e, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x151b, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x150a, 0x005d, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1508, 0x0010, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1205, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1206, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1201, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1201, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0000, 1); // snd_hda + +} + +static void cs42l83_headset_power_on_on_nouse(struct hda_codec *codec) +{ + // this function replaced by cs42l83_power_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_power_on_on_nouse\n"); + + // AppleHDATDM_CS42L83::powerOn + + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0700 i2c data 0x0720 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0701 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1207, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1207, 0x0020, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0001, 1); // snd_hda + +} + +static void cs42l83_power_onoff(struct hda_codec *codec, bool onflag) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_onoff\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::powerOn + + // bool arg switches order of 0x1107/0x1501 - on (0x1207) 0x1501/0x1107 - off 0x1107/0x1501 + + if (onflag) + { + // register 0x1207 - ASP Clock Configuration 1 + // changed from 0x00 to 0x20 - ASP_LCPOL_OUT inverted + // register 0x1501 - PLL Control 1 + // set to 0x01 - powered on + // register 0x1107 - Oscillator Switch Control + // set to 0x01 - SCLK is present and the internal MCLK is sourced from the SCLK pin + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0700 i2c data 0x0700 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0701 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1207, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1207, 0x0020, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0001, 1); // snd_hda + } + else + { + // register 0x1107 - Oscillator Switch Control + // set to 0x00 (power down, in transition) + // register 0x1501 - PLL Control 1 + // set to 0x00 (powered off) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0000, 1); // snd_hda + } +} + +static void cs42l83_configure_serial_port(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_configure_serial_port\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_configureSerialPort + + // register 0x2505 - Serial Port Transmit Isochronous Control + // changed from 0x04 to 0x05 (NFS mode, 48k isochronous stream) + // register 0x2502 - Serial Port Receive Isochronous Control + // changed from 0x04 to 0x05 (NFS mode, 48k isochronous stream) + // register 0x2503 - Serial Port Receive Sample Rate + // changed from 0x8c to 0x8a (48 kHz to 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xcc to 0xca (48 kHz to 44.1 kHz) + // register 0x1208 - ASP Frame Configuration + // changed from 0x00 to 0x02 (ASP_STP from high to low, ASP frame start 1.0 delay) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0500 i2c data 0x0504 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x0505 i2c data 0x0005 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0200 i2c data 0x0204 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x0205 i2c data 0x0005 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0300 i2c data 0x038c +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x038a i2c data 0x008a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06cc +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0800 i2c data 0x0800 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0802 i2c data 0x0002 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2505, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2505, 0x0005, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2502, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2502, 0x0005, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2503, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2503, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1208, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0002, 1); // snd_hda + +} + +static void cs42l83_output_set_input_sample_rate(struct hda_codec *codec) +{ + // changed name from cs42l83_set_sample_rate + // NOTA BENE - it says output sample rate because its the sample rate for digital data + // coming into the cs42l83 and being converted to analog output + // - so the register is labelled the Input sample rate!! + int ret_fs_rate; + + mycodec_i2c_local_info(codec, "cs42l83_output_set_input_sample_rate\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // register 0x2601 - SRC Input Sample Rate + // changed from 0x40 to 0x4a (0x0a is 44.1 kHz) + // register 0x2503 - Serial Port Receive Sample Rate + // changed from 0x8a to 0x8a (0x0a is 44.1 kHz) + // register 0x120a - Input ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x01 (0x01 Enable IASRC 96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0100 i2c data 0x0140 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x014a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0300 i2c data 0x038a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x038a i2c data 0x008a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0a00 i2c data 0x0a00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0a00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0900 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0901 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2601, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2601, 0x004a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2503, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2503, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x120a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120a, 0x0000, 1); // snd_hda + + // correctly implement this to mask in the right bit as per docs + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0001, 1); // snd_hda + ret_fs_rate = cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + ret_fs_rate |= 0x1; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, ret_fs_rate, 1); // snd_hda + +} + +static void cs42l83_setup_audio_output(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_setup_audio_output\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioOutput + + // register 0x1f06 - DAC Control 2 + // set to 0x2 (default) DAC HPF enable + // register 0x2001 - HP Control + // changed from 0x0d (default) to 0x0d + // register 0x2a04 - ASP Receive DAI0 Channel 1 Bit Start LSB + // set to 0x00 + // register 0x2a07 - ASP Receive DAI0 Channel 2 Bit Start LSB + // set to 0x20 (is this 32 bits?? - maybe) + // register 0x2301 - Mixer Channel A Input Volume + // set to 0x00 (0 dB - positive values negative dB as usual 0x3f mute (default)) + // register 0x2303 - Mixer Channel B Input Volume + // set to 0x00 (0 dB - positive values negative dB as usual 0x3f mute (default)) + // register 0x2a02 - ASP Receive DAI0 Channel 1 Phase and Resolution + // set to 0x02 (24 bits per sample) + // (0x00 8 bits, 0x01 16 bits, 0x03 32 bits (default)) + // register 0x2a05 - ASP Receive DAI0 Channel 2 Phase and Resolution + // set to 0x02 (24 bits per sample) + // (0x00 8 bits, 0x01 16 bits, 0x03 32 bits (default)) + // register 0x100b - Slow Start Enable + // changed from 0x70 (default) to 0x00 (disabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0602 i2c data 0x0002 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x010d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x010d i2c data 0x000d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0400 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x0300 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0202 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0502 i2c data 0x0002 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0b00 i2c data 0x0b70 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0b00 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x000d, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a04, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a07, 0x0020, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2301, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2303, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a02, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a05, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x100b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x100b, 0x0000, 1); // snd_hda + +} + + +static void cs42l83_headset_rcv_enable_on(struct hda_codec *codec) +{ + int retval; + + // this function has been replaced by cs42l83_buffers_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_rcv_enable_on\n"); + + // AppleHDATDM_CS42L83::enable + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x010c i2c data 0x000c + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x000c, 1); // snd_hda + +} + +static void cs42l83_buffers_onoff(struct hda_codec *codec, bool onflag) +{ + + mycodec_i2c_local_info(codec, "cs42l83_buffers_onoff\n"); + + // in AppleHDATDM_CS42L83::enable + + // yet again this has a bool arg which is enable/disable + + if (onflag) + { + // register 0x2a01 - ASP Receive Enable + // set to 0x0c (populate channel buffers for 1 and 2) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x010c i2c data 0x000c + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x000c, 1); // snd_hda + } + else + { + // register 0x2a01 - ASP Receive Enable + // set to 0x00 (channel buffers not populated) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x0000, 1); // snd_hda + } +} + +static void cs42l83_set_power_state_on_nouse(struct hda_codec *codec, int dummy) +{ + + mycodec_i2c_local_info(codec, "cs42l83_set_power_state_on_nouse\n"); + + // AppleHDATDM_CS42L83::setPowerState + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01fe +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b24 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + +} + + +static void cs42l83_headset_enable_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_enable_on\n"); + + // looks as tho this is only enabling the headphone component + // - doesnt seem to be needed for the headset mike to work + // (unless it just happens the headphone component is always enabled so + // dont see it for the headset mike) + + // AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::enableHeadphones + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e99 i2c data 0x0099 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x0830 i2c data 0x0030 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x21 lo 0x0200 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x010d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0099, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b08, 0x0030, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2102, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0000, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x0001, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x2001, 0x0c, 0x00, 1); // snd_hda + +} + + +static void cs42l83_plugin_interrupt_setup(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_plugin_interrupt_setup\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // this enables the headphone plugin sense interrupt + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Interrupt + // now think this shows which interrupt of 0x1b79 was triggered + // reading this clears any interrupt which will send a UR + // value 0x00 + // register 0x1b79 - Detect Interrupt Mask 1 + // changed from 0xe0 (default) to 0xa0 - unmasks TIP_SENSE_PLUG + // changed from 0xc0 (TIP_SENSE_UNPLUG unmasked) to 0xa0 (TIP_SENSE_UNPLUG masked TIP_SENSE_PLUG unmasked) + + // so reading 0x1b7b seems to lead to 2 UNSOL events + // one with interrupt set followed by one with interrupt cleared + + mycodec_info(codec, "command cs42l83_plugin_interrupt_setup start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79c0 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x79a0 i2c data 0x00a0 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + newval = (retval & 0x9f) | 0x20; + + myprintk_dbg("snd_hda_intel: cs42l83_plugin_interrupt_setup 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b79,newval,0x00a0,retval,0x00c0); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, 0x00a0, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_plugin_interrupt_setup end\n"); +} + +static void cs42l83_headset_detect2_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect2_off\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // register 0x1b7c - Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xff state of 0x1b7a) + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff to 0xff (all masked) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x3 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + newval = (retval | 0x38); + + myprintk_dbg("snd_hda_intel: cs42l83_headset_detect2_off 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b7a,newval,0x00ff,retval,0x00ff); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + newval = (retval & 0xe7); + + myprintk_dbg("snd_hda_intel: cs42l83_headset_detect2_off 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b7c,newval,0x00a0,retval,0x00c0); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off3(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off3\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0xc6 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x70c6 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_disable_button_interrupts(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_disable_button_interrupts\n"); + + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x04 + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xfc + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xff + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xff + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c04 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7afc +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + +} + +static void cs42l83_unplug_headset_detect_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_unplug_headset_detect_off\n"); + + // this is AppleHDAMikeyInternalCS8409::enableHSDetection + // this calls readMikey/writeMikey with 0x8 arg and 0x9 arg + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 to 0x82 + // 0x80 headset detection automatic, disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_headset_switch_control(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_switch_control\n"); + + // from AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // this is likely writeMikey 0x9, 0xa making it plausibly AppleHDAMikeyInternalCS8409::configureForUSHeadset + + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 to 0x42 + // 0x40 headset detection manual, 0x2 HSDET_AUTO_TIME 50 microsecs + + // I think this is writeMikey with 0xa arg + + // register 0x1121 - Headset Switch Control + // set to 0xa6 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2042 i2c data 0x0042 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x21a6 i2c data 0x00a6 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0042, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1121, 0x00a6, 1); // snd_hda + +} + + +static void cs42l83_headset_enable_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_enable_off\n"); + + // in AppleHDATDM_CS42L83::enable - 0x22cb + // from AppleHDATDM_CS42L83::_powerOffCodecOutput + // AppleHDATDM_CS42L83::enableHeadphones(bool) + + // register 0x2001 - HP Control + // changed from 0x01 to 0x0d + // 0x107e - undocumented Global + // set to 0x99 + // 0x1b08 - undocumented Headset Interface + // set to 0x10 + // 0x2102 - Class H?? DPn Port Control?? + // set to 0x20 + // 0x107e - undocumented Global + // set to 0x00 + + myprintk("snd_hda_intel: cs42l83_headset_enable_off start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x0101 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x010d i2c data 0x000d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e99 i2c data 0x0099 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x0810 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x21 lo 0x0220 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e00 i2c data 0x0000 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x000d, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x2001, 0x0c, 0x0c, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0099, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b08, 0x0010, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2102, 0x0020, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0000, 1); // snd_hda + + myprintk("snd_hda_intel: cs42l83_headset_enable_off end\n"); + +} + + +static void cs42l83_power_off_codec_output(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_off_codec_output\n"); + + // in AppleHDATDM_CS42L83::enable or AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::_powerOffCodecOutput + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0x96 to 0x9e (0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered to + // 0x40 ASP Input, 0x20 Mixer, 0x1 codec powered) + // register 0x1101 - Power Down Control 1 + // changed from 0x9e (0x40 ASP Input, 0x20 Mixer, 0x1 codec powered) to 0xfe (default - 0x1 codec powered)) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0196 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x01fe i2c data 0x00fe + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x08, 0x08, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x00fe, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x60, 0x60, 1); // snd_hda +} + +static void cs42l83_power_off_codec_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_off_codec_input\n"); + + // in AppleHDATDM_CS42L83::enable or AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::_powerOffCodecInput + + // note there is a call to AppleHDATDM_CS42L83::enableExtMicrophone(0) in this routine + // ah - thats the 0x1d03 mute/unmute volume register + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0x12 to 0x16 (0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x4 ADC, 0x1 codec powered to + // 0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPout, 0x1 codec powered) + // register 0x1101 - Power Down Control 1 + // changed from 0x16 (0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered) to + // 0x96 (0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0112 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0116 i2c data 0x0016 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0116 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0016, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x04, 0x04, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x80, 0x80, 1); // snd_hda +} + +static void cs42l83_headset_rcv_enable_off(struct hda_codec *codec) +{ + // this function has been replaced by cs42l83_buffers_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_rcv_enable_off\n"); + + // in AppleHDATDM_CS42L83::enable + // this is only place we have a 0x2a01 + + // yet again this has a bool arg which is enable/disable + + // register 0x2a01 - ASP Receive Enable + // set to 0x00 (channel buffers not populated) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x0000, 1); // snd_hda + +} + +static void cs42l83_headset_power_off(struct hda_codec *codec) +{ + // this function replaced by cs42l83_power_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_power_off\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::powerOn + // likely power off - bool arg switches order of 0x1107/0x1501 - on (0x2a10) 0x1501/0x1107 - off 0x1107/0x1501 + + // register 0x1107 - Oscillator Switch Control + // set to 0x00 (power down, in transition) + // register 0x1501 - PLL Control 1 + // set to 0x00 (powered off) + + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0000, 1); // snd_hda + +} + +//static void cs42l83_line_or_mike_set_sample_rate(struct hda_codec *codec) +static void cs42l83_input_set_output_sample_rate(struct hda_codec *codec) +{ + // changed name from cs42l83_line_or_mike_set_sample_rate + // NOTA BENE - it says input sample rate because its the sample rate for data + // from analog inputs converted to digital and going out of the cs42l83 + // - so the register is labelled the Output sample rate!! + + int ret_fs_rate; + + mycodec_i2c_local_info(codec, "cs42l83_input_set_output_sample_rate\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // is this for line or mike?? + + // register 0x2609 - SRC Output Sample Rate + // changed from 0x40 to 0x4a (0x0a is 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xca to 0xca (0x0a is 44.1 kHz) + // register 0x120b - Output ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x03 (0x01 Enable IASRC 96K and lower rates, 0x02 Enable OASRC96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0900 i2c data 0x0940 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x094a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06ca +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0b00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0901 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0903 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2609, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2609, 0x004a, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x120b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120b, 0x0000, 1); // snd_hda + + // correctly implement this to mask in the right bit as per docs + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0003, 1); // snd_hda + ret_fs_rate = cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + ret_fs_rate |= 0x2; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, ret_fs_rate, 1); // snd_hda + +} + +static void cs42l83_mike_setup_audio_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_setup_audio_input\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioInput + + // register 0x2903 - ASP Transmit Channel Phase and Resolution + // register 0x2905 - ASP Transmit Channel 1 Bit Start LSB + // register 0x290b - ASP Transmit Channel 2 Bit Start LSB + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x29 lo 0x0300 i2c data 0x030f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x030a i2c data 0x000a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0500 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0b20 i2c data 0x0020 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2903, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2903, 0x000a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2905, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x290b, 0x0020, 1); // snd_hda + +} + +static void cs42l83_mike_enable(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_enable\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x03 (ASP Transmit Channel 1 enable (0x01), ASP Transmit Channel 2 enable (0x02)) + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x01 (Enabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0203 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0001, 1); // snd_hda + +} + + +static void cs42l83_mike_disable(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_disable\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x00 (Disabled) + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x00 (disabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0200 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0000, 1); // snd_hda + +} + +//static void cs42l83_line_or_mike_set_sample_rate1(struct hda_codec *codec) +static void cs42l83_input_set_output_sample_rate1(struct hda_codec *codec) +{ + // changed name from cs42l83_line_or_mike_set_sample_rate1 + // NOTA BENE - it says input sample rate because its the sample rate for data + // from analog inputs converted to digital and going out of the cs42l83 + // - so the register is labelled the Output sample rate!! + // NOTA BENE - NOT fixed to do bit op for 0x1209 register!!! + + mycodec_i2c_local_info(codec, "cs42l83_input_set_output_sample_rate1\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // is this for line or mike?? + + // register 0x2609 - SRC Output Sample Rate + // changed from 0x4a to 0x4a (0x0a is 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xca to 0xca (0x0a is 44.1 kHz) + // register 0x120b - Output ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x01 (0x01 Enable IASRC 96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0900 i2c data 0x094a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x094a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06ca +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0b00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0903 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0903 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2609, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2609, 0x004a, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x120b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120b, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0003, 1); // snd_hda + +} + +static void cs42l83_line_or_mike_setup_audio_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_line_or_mike_setup_audio_input\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioInput + + // register 0x2903 - ASP Transmit Channel Phase and Resolution + // register 0x2905 - ASP Transmit Channel 1 Bit Start LSB + // register 0x290b - ASP Transmit Channel 2 Bit Start LSB + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x29 lo 0x0300 i2c data 0x030a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x030a i2c data 0x000a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0500 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0b20 i2c data 0x0020 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2903, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2903, 0x000a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2905, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x290b, 0x0020, 1); // snd_hda + +} + +static void cs42l83_line_or_mike_enable_nouse(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_line_or_mike_enable_nouse\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x03 (ASP Transmit Channel 1 enable (0x01), ASP Transmit Channel 2 enable (0x02)) + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x01 (Enabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0203 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0001, 1); // snd_hda + +} + +static void cs42l83_headset_mike_pin_enable(struct hda_codec *codec) +{ + int retval = 0; + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_pin_enable\n"); + + // why twice?? + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 14146); // 0x03cf0700 + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 14147); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + +} + +static void cs42l83_configure_headset_button_interrupts(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_configure_headset_button_interrupts\n"); + + // from AppleHDAMikeyInternalCS8409::handleButtonDetectUR(unsigned int) + + // AppleHDAMikeyInternalCS8409::configureHeadset + // - this calls 0x1b7c and 0x1b7a + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff to 0xdc (0x23 unmasked, 0x18 masked - undocumented - maybe mike buttons - rest unmasked) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c40 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7adc i2c data 0x00dc + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00dc, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off2(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off2\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_hsbias_sense_on(struct hda_codec *codec) +{ + + int updval; + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_hsbias_sense_on\n"); + + // in AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0xc6 + // set to 0xc6 (HS Bias Sense Enable (0x80) Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x70c6 i2c data 0x00c6 + + // explicit coding + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x00c6, 1); // snd_hda + + // bit coding + updval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + updval |= 0x80; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + + +static void cs42l83_headset_mike_adc_unmutevol(struct hda_codec *codec, int unmute) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_adc_unmutevol\n"); + + // in AppleHDATDM_CS42L83::enableExtMicrophone + + if (unmute) + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1d03, 0x0000, 1); // snd_hda + else + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1d03, 0x0080, 1); // snd_hda +} + diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c new file mode 100644 index 0000000..0924013 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Lucas Tanure + */ + +#include "patch_cs8409.h" + +/****************************************************************************** + * CS42L42 Specific Data + * + ******************************************************************************/ + +static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale, CS42L42_HP_VOL_REAL_MIN * 100, 100, 1); + +static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale, CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1); + +const struct snd_kcontrol_new cs42l42_dac_volume_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .index = 0, + .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .info = cs42l42_volume_info, + .get = cs42l42_volume_get, + .put = cs42l42_volume_put, + .tlv = { .p = cs42l42_dac_db_scale }, + .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0, + HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE +}; + +const struct snd_kcontrol_new cs42l42_adc_volume_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .index = 0, + .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .info = cs42l42_volume_info, + .get = cs42l42_volume_get, + .put = cs42l42_volume_put, + .tlv = { .p = cs42l42_adc_db_scale }, + .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0, + HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE +}; + +const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback = { + .rates = SNDRV_PCM_RATE_48000, /* fixed rate */ +}; + +const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture = { + .rates = SNDRV_PCM_RATE_48000, /* fixed rate */ +}; + +/****************************************************************************** + * BULLSEYE / WARLOCK / CYBORG Specific Arrays + * CS8409/CS42L42 + ******************************************************************************/ + +const struct hda_verb cs8409_cs42l42_init_verbs[] = { + { CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */ + {} /* terminator */ +}; + +static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = { + { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */ + { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */ + { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ + { CS8409_PIN_DMIC1_IN, 0x90a00090 }, /* DMIC-1 */ + {} /* terminator */ +}; + +static const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = { + { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */ + { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */ + { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ + {} /* terminator */ +}; + +/* Vendor specific HW configuration for CS42L42 */ +static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { + { CS42L42_I2C_TIMEOUT, 0xB0 }, + { CS42L42_ADC_CTL, 0x00 }, + { 0x1D02, 0x06 }, + { CS42L42_ADC_VOLUME, 0x9F }, + { CS42L42_OSC_SWITCH, 0x01 }, + { CS42L42_MCLK_CTL, 0x02 }, + { CS42L42_SRC_CTL, 0x03 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x13 }, + { CS42L42_FSYNC_P_LOWER, 0xFF }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x20 }, + { CS42L42_SPDIF_CLK_CFG, 0x0D }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 }, + { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 }, + { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 }, + { CS42L42_ASP_RX_DAI0_EN, 0x0C }, + { CS42L42_ASP_TX_CH_EN, 0x01 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_SZ_EN, 0x01 }, + { CS42L42_PWR_CTL1, 0x0A }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3f }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_MIC_DET_CTL1, 0xB6 }, + { CS42L42_TIPSENSE_CTL, 0xC2 }, + { CS42L42_HS_CLAMP_DISABLE, 0x01 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x00 }, + { CS42L42_TSENSE_CTL, 0x80 }, + { CS42L42_HS_BIAS_CTL, 0xC0 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, + { CS42L42_ADC_OVFL_INT_MASK, 0xff }, + { CS42L42_MIXER_INT_MASK, 0xff }, + { CS42L42_SRC_INT_MASK, 0xff }, + { CS42L42_ASP_RX_INT_MASK, 0xff }, + { CS42L42_ASP_TX_INT_MASK, 0xff }, + { CS42L42_CODEC_INT_MASK, 0xff }, + { CS42L42_SRCPL_INT_MASK, 0xff }, + { CS42L42_VPMON_INT_MASK, 0xff }, + { CS42L42_PLL_LOCK_INT_MASK, 0xff }, + { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, + { CS42L42_DET_INT1_MASK, 0xff }, + { CS42L42_DET_INT2_MASK, 0xff }, +}; + +/* Vendor specific hw configuration for CS8409 */ +const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = { + /* +PLL1/2_EN, +I2C_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, + /* ASP1/2_EN=0, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, + /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, + /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 }, + /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 }, + /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 }, + /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 }, + /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 }, + /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 }, + /* ASP1: LCHI = 00h */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, + /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, + /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, + /* ASP2: LCHI=1Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f }, + /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f }, + /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c }, + /* DMIC1_MO=10b, DMIC1/2_SR=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DMIC_CFG, 0x0023 }, + /* ASP1/2_BEEP=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, + /* ASP1/2_EN=1, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 }, + /* -PLL2_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, + /* TX2.A: pre-scale att.=0 dB */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 }, + /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 }, + /* test mode on */ + { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 }, + /* GPIO hysteresis = 30 us */ + { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 }, + /* test mode off */ + { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 }, + {} /* Terminator */ +}; + +const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = { + /* EQ_SEL=1, EQ1/2_EN=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4000 }, + /* +EQ_ACC */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x4000 }, + /* +EQ2_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4010 }, + /* EQ_DATA_HI=0x0647 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc0c7 }, + /* EQ_DATA_HI=0x0647 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc1c7 }, + /* EQ_DATA_HI=0xf370 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xf370 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc271 }, + /* EQ_DATA_HI=0x1ef8 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ef8 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc348 }, + /* EQ_DATA_HI=0xc110 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc110 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc45a }, + /* EQ_DATA_HI=0x1f29 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1f29 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc574 }, + /* EQ_DATA_HI=0x1d7a */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1d7a }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc653 }, + /* EQ_DATA_HI=0xc38c */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc714 }, + /* EQ_DATA_HI=0x1ca3 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ca3 }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc8c7 }, + /* EQ_DATA_HI=0xc38c */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c }, + /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc914 }, + /* -EQ_ACC, -EQ_WRT */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 }, + {} /* Terminator */ +}; + +struct sub_codec cs8409_cs42l42_codec = { + .addr = CS42L42_I2C_ADDR, + .reset_gpio = CS8409_CS42L42_RESET, + .irq_mask = CS8409_CS42L42_INT, + .init_seq = cs42l42_init_reg_seq, + .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq), + .hp_jack_in = 0, + .mic_jack_in = 0, + .paged = 1, + .suspended = 1, + .no_type_dect = 0, +}; + +/****************************************************************************** + * Dolphin Specific Arrays + * CS8409/ 2 X CS42L42 + ******************************************************************************/ + +const struct hda_verb dolphin_init_verbs[] = { + { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, DOLPHIN_WAKE }, /* WAKE from GPIO 0,4 */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */ + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */ + {} /* terminator */ +}; + +static const struct hda_pintbl dolphin_pincfgs[] = { + { 0x24, 0x022210f0 }, /* ASP-1-TX-A */ + { 0x25, 0x010240f0 }, /* ASP-1-TX-B */ + { 0x34, 0x02a21050 }, /* ASP-1-RX */ + {} /* terminator */ +}; + +/* Vendor specific HW configuration for CS42L42 */ +static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { + { CS42L42_I2C_TIMEOUT, 0xB0 }, + { CS42L42_ADC_CTL, 0x00 }, + { 0x1D02, 0x06 }, + { CS42L42_ADC_VOLUME, 0x9F }, + { CS42L42_OSC_SWITCH, 0x01 }, + { CS42L42_MCLK_CTL, 0x02 }, + { CS42L42_SRC_CTL, 0x03 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x13 }, + { CS42L42_FSYNC_P_LOWER, 0xFF }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x20 }, + { CS42L42_SPDIF_CLK_CFG, 0x0D }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 }, + { CS42L42_ASP_RX_DAI0_EN, 0x0C }, + { CS42L42_ASP_TX_CH_EN, 0x01 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_SZ_EN, 0x01 }, + { CS42L42_PWR_CTL1, 0x0A }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3f }, + { CS42L42_MIC_DET_CTL1, 0xB6 }, + { CS42L42_TIPSENSE_CTL, 0xC2 }, + { CS42L42_HS_CLAMP_DISABLE, 0x01 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x00 }, + { CS42L42_TSENSE_CTL, 0x80 }, + { CS42L42_HS_BIAS_CTL, 0xC0 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, + { CS42L42_ADC_OVFL_INT_MASK, 0xff }, + { CS42L42_MIXER_INT_MASK, 0xff }, + { CS42L42_SRC_INT_MASK, 0xff }, + { CS42L42_ASP_RX_INT_MASK, 0xff }, + { CS42L42_ASP_TX_INT_MASK, 0xff }, + { CS42L42_CODEC_INT_MASK, 0xff }, + { CS42L42_SRCPL_INT_MASK, 0xff }, + { CS42L42_VPMON_INT_MASK, 0xff }, + { CS42L42_PLL_LOCK_INT_MASK, 0xff }, + { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, + { CS42L42_DET_INT1_MASK, 0xff }, + { CS42L42_DET_INT2_MASK, 0xff } +}; + +static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { + { CS42L42_I2C_TIMEOUT, 0xB0 }, + { CS42L42_ADC_CTL, 0x00 }, + { 0x1D02, 0x06 }, + { CS42L42_ADC_VOLUME, 0x9F }, + { CS42L42_OSC_SWITCH, 0x01 }, + { CS42L42_MCLK_CTL, 0x02 }, + { CS42L42_SRC_CTL, 0x03 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x13 }, + { CS42L42_FSYNC_P_LOWER, 0xFF }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x20 }, + { CS42L42_SPDIF_CLK_CFG, 0x0D }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 }, + { CS42L42_ASP_RX_DAI0_EN, 0x0C }, + { CS42L42_ASP_TX_CH_EN, 0x00 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_SZ_EN, 0x00 }, + { CS42L42_PWR_CTL1, 0x0E }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3f }, + { CS42L42_MIC_DET_CTL1, 0xB6 }, + { CS42L42_TIPSENSE_CTL, 0xC2 }, + { CS42L42_HS_CLAMP_DISABLE, 0x01 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x00 }, + { CS42L42_TSENSE_CTL, 0x80 }, + { CS42L42_HS_BIAS_CTL, 0xC0 }, + { CS42L42_PWR_CTL1, 0x06, 10000 }, + { CS42L42_ADC_OVFL_INT_MASK, 0xff }, + { CS42L42_MIXER_INT_MASK, 0xff }, + { CS42L42_SRC_INT_MASK, 0xff }, + { CS42L42_ASP_RX_INT_MASK, 0xff }, + { CS42L42_ASP_TX_INT_MASK, 0xff }, + { CS42L42_CODEC_INT_MASK, 0xff }, + { CS42L42_SRCPL_INT_MASK, 0xff }, + { CS42L42_VPMON_INT_MASK, 0xff }, + { CS42L42_PLL_LOCK_INT_MASK, 0xff }, + { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, + { CS42L42_DET_INT1_MASK, 0xff }, + { CS42L42_DET_INT2_MASK, 0xff } +}; + +/* Vendor specific hw configuration for CS8409 */ +const struct cs8409_cir_param dolphin_hw_cfg[] = { + /* +PLL1/2_EN, +I2C_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, + /* ASP1_EN=0, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, + /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, + /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 }, + /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 }, + /* ASP1.B: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=128 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL1, 0x0880 }, + /* ASP1.B: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=160 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL2, 0x08a0 }, + /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 }, + /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 }, + /* ASP1: LCHI = 00h */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, + /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, + /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, + /* ASP1/2_BEEP=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, + /* ASP1_EN=1, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0022 }, + /* -PLL2_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, + /* ASP1_xxx_EN=1, ASP1_MCLK_EN=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0x5400 }, + /* test mode on */ + { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 }, + /* GPIO hysteresis = 30 us */ + { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 }, + /* test mode off */ + { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 }, + {} /* Terminator */ +}; + +struct sub_codec dolphin_cs42l42_0 = { + .addr = DOLPHIN_C0_I2C_ADDR, + .reset_gpio = DOLPHIN_C0_RESET, + .irq_mask = DOLPHIN_C0_INT, + .init_seq = dolphin_c0_init_reg_seq, + .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq), + .hp_jack_in = 0, + .mic_jack_in = 0, + .paged = 1, + .suspended = 1, + .no_type_dect = 0, +}; + +struct sub_codec dolphin_cs42l42_1 = { + .addr = DOLPHIN_C1_I2C_ADDR, + .reset_gpio = DOLPHIN_C1_RESET, + .irq_mask = DOLPHIN_C1_INT, + .init_seq = dolphin_c1_init_reg_seq, + .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq), + .hp_jack_in = 0, + .mic_jack_in = 0, + .paged = 1, + .suspended = 1, + .no_type_dect = 1, +}; + +/****************************************************************************** + * CS8409 Patch Driver Structs + * Arrays Used for all projects using CS8409 + ******************************************************************************/ + +const struct hda_quirk cs8409_fixup_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE), + SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0ACF, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0AD0, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0AD1, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0AD2, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0AD3, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG), + SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0B92, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0B93, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0B94, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN), + SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN), + SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN), + SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN), + SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN), + SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0BB6, "Warlock V3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0BB8, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BB9, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0BBA, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BBB, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0BBC, "Warlock MLK", CS8409_WARLOCK_MLK), + SND_PCI_QUIRK(0x1028, 0x0BBD, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), + SND_PCI_QUIRK(0x1028, 0x0BD4, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0BD5, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0BD6, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0BD7, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0BD8, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C43, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C50, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C51, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C52, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C73, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C75, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C7D, "Dolphin", CS8409_DOLPHIN), + SND_PCI_QUIRK(0x1028, 0x0C7F, "Dolphin", CS8409_DOLPHIN), + {} /* terminator */ +}; + +/* Dell Inspiron models with cs8409/cs42l42 */ +const struct hda_model_fixup cs8409_models[] = { + { .id = CS8409_BULLSEYE, .name = "bullseye" }, + { .id = CS8409_WARLOCK, .name = "warlock" }, + { .id = CS8409_WARLOCK_MLK, .name = "warlock mlk" }, + { .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" }, + { .id = CS8409_CYBORG, .name = "cyborg" }, + { .id = CS8409_DOLPHIN, .name = "dolphin" }, + { .id = CS8409_ODIN, .name = "odin" }, + {} +}; + +const struct hda_fixup cs8409_fixups[] = { + [CS8409_BULLSEYE] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, + [CS8409_WARLOCK] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, + [CS8409_WARLOCK_MLK] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, + [CS8409_WARLOCK_MLK_DUAL_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, + [CS8409_CYBORG] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, + [CS8409_FIXUPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs8409_cs42l42_fixups, + }, + [CS8409_DOLPHIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = dolphin_pincfgs, + .chained = true, + .chain_id = CS8409_DOLPHIN_FIXUPS, + }, + [CS8409_DOLPHIN_FIXUPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = dolphin_fixups, + }, + [CS8409_ODIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cs42l42_pincfgs_no_dmic, + .chained = true, + .chain_id = CS8409_FIXUPS, + }, +}; diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c new file mode 100644 index 0000000..7a720a8 --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c @@ -0,0 +1,1595 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "patch_cs8409.h" + +/****************************************************************************** + * CS8409 Specific Functions + ******************************************************************************/ + +static int cs8409_parse_auto_config(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + int err; + int i; + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + /* keep the ADCs powered up when it's dynamically switchable */ + if (spec->gen.dyn_adc_switch) { + unsigned int done = 0; + + for (i = 0; i < spec->gen.input_mux.num_items; i++) { + int idx = spec->gen.dyn_adc_idx[i]; + + if (done & (1 << idx)) + continue; + snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]); + done |= 1 << idx; + } + } + + return 0; +} + +static void cs8409_disable_i2c_clock_worker(struct work_struct *work); + +static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec) +{ + struct cs8409_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + codec->spec = spec; + spec->codec = codec; + codec->power_save_node = 1; + mutex_init(&spec->i2c_mux); + INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + +static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0); +} + +static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef); +} + +/* + * cs8409_enable_i2c_clock - Disable I2C clocks + * @codec: the codec instance + * Disable I2C clocks. + * This must be called when the i2c mutex is unlocked. + */ +static void cs8409_disable_i2c_clock(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + mutex_lock(&spec->i2c_mux); + if (spec->i2c_clck_enabled) { + cs8409_vendor_coef_set(spec->codec, 0x0, + cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7); + spec->i2c_clck_enabled = 0; + } + mutex_unlock(&spec->i2c_mux); +} + +/* + * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use + */ +static void cs8409_disable_i2c_clock_worker(struct work_struct *work) +{ + struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work); + + cs8409_disable_i2c_clock(spec->codec); +} + +/* + * cs8409_enable_i2c_clock - Enable I2C clocks + * @codec: the codec instance + * Enable I2C clocks. + * This must be called when the i2c mutex is locked. + */ +static void cs8409_enable_i2c_clock(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + /* Cancel the disable timer, but do not wait for any running disable functions to finish. + * If the disable timer runs out before cancel, the delayed work thread will be blocked, + * waiting for the mutex to become unlocked. This mutex will be locked for the duration of + * any i2c transaction, so the disable function will run to completion immediately + * afterwards in the scenario. The next enable call will re-enable the clock, regardless. + */ + cancel_delayed_work(&spec->i2c_clk_work); + + if (!spec->i2c_clck_enabled) { + cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8); + spec->i2c_clck_enabled = 1; + } + queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25)); +} + +/** + * cs8409_i2c_wait_complete - Wait for I2C transaction + * @codec: the codec instance + * + * Wait for I2C transaction to complete. + * Return -ETIMEDOUT if transaction wait times out. + */ +static int cs8409_i2c_wait_complete(struct hda_codec *codec) +{ + unsigned int retval; + + return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18, + CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS); +} + +/** + * cs8409_set_i2c_dev_addr - Set i2c address for transaction + * @codec: the codec instance + * @addr: I2C Address + */ +static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr) +{ + struct cs8409_spec *spec = codec->spec; + + if (spec->dev_addr != addr) { + cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr); + spec->dev_addr = addr; + } +} + +/** + * cs8409_i2c_set_page - CS8409 I2C set page register. + * @scodec: the codec instance + * @i2c_reg: Page register + * + * Returns negative on error. + */ +static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg) +{ + struct hda_codec *codec = scodec->codec; + + if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) { + cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8); + if (cs8409_i2c_wait_complete(codec) < 0) + return -EIO; + scodec->last_page = i2c_reg >> 8; + } + + return 0; +} + +/** + * cs8409_i2c_read - CS8409 I2C Read. + * @scodec: the codec instance + * @addr: Register to read + * + * Returns negative on error, otherwise returns read value in bits 0-7. + */ +static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr) +{ + struct hda_codec *codec = scodec->codec; + struct cs8409_spec *spec = codec->spec; + unsigned int i2c_reg_data; + unsigned int read_data; + + if (scodec->suspended) + return -EPERM; + + mutex_lock(&spec->i2c_mux); + cs8409_enable_i2c_clock(codec); + cs8409_set_i2c_dev_addr(codec, scodec->addr); + + if (cs8409_i2c_set_page(scodec, addr)) + goto error; + + i2c_reg_data = (addr << 8) & 0x0ffff; + cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); + if (cs8409_i2c_wait_complete(codec) < 0) + goto error; + + /* Register in bits 15-8 and the data in 7-0 */ + read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD); + + mutex_unlock(&spec->i2c_mux); + + return read_data & 0x0ff; + +error: + mutex_unlock(&spec->i2c_mux); + codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); + return -EIO; +} + +/** + * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence. + * @scodec: the codec instance + * @seq: Register Sequence to read + * @count: Number of registeres to read + * + * Returns negative on error, values are read into value element of cs8409_i2c_param sequence. + */ +static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count) +{ + struct hda_codec *codec = scodec->codec; + struct cs8409_spec *spec = codec->spec; + unsigned int i2c_reg_data; + int i; + + if (scodec->suspended) + return -EPERM; + + mutex_lock(&spec->i2c_mux); + cs8409_set_i2c_dev_addr(codec, scodec->addr); + + for (i = 0; i < count; i++) { + cs8409_enable_i2c_clock(codec); + if (cs8409_i2c_set_page(scodec, seq[i].addr)) + goto error; + + i2c_reg_data = (seq[i].addr << 8) & 0x0ffff; + cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); + + if (cs8409_i2c_wait_complete(codec) < 0) + goto error; + + seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff; + } + + mutex_unlock(&spec->i2c_mux); + + return 0; + +error: + mutex_unlock(&spec->i2c_mux); + codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); + return -EIO; +} + +/** + * cs8409_i2c_write - CS8409 I2C Write. + * @scodec: the codec instance + * @addr: Register to write to + * @value: Data to write + * + * Returns negative on error, otherwise returns 0. + */ +static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value) +{ + struct hda_codec *codec = scodec->codec; + struct cs8409_spec *spec = codec->spec; + unsigned int i2c_reg_data; + + if (scodec->suspended) + return -EPERM; + + mutex_lock(&spec->i2c_mux); + + cs8409_enable_i2c_clock(codec); + cs8409_set_i2c_dev_addr(codec, scodec->addr); + + if (cs8409_i2c_set_page(scodec, addr)) + goto error; + + i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff); + cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); + + if (cs8409_i2c_wait_complete(codec) < 0) + goto error; + + mutex_unlock(&spec->i2c_mux); + return 0; + +error: + mutex_unlock(&spec->i2c_mux); + codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); + return -EIO; +} + +/** + * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence. + * @scodec: the codec instance + * @seq: Register Sequence to write + * @count: Number of registeres to write + * + * Returns negative on error. + */ +static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq, + int count) +{ + struct hda_codec *codec = scodec->codec; + struct cs8409_spec *spec = codec->spec; + unsigned int i2c_reg_data; + int i; + + if (scodec->suspended) + return -EPERM; + + mutex_lock(&spec->i2c_mux); + cs8409_set_i2c_dev_addr(codec, scodec->addr); + + for (i = 0; i < count; i++) { + cs8409_enable_i2c_clock(codec); + if (cs8409_i2c_set_page(scodec, seq[i].addr)) + goto error; + + i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff); + cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); + + if (cs8409_i2c_wait_complete(codec) < 0) + goto error; + /* Certain use cases may require a delay + * after a write operation before proceeding. + */ + if (seq[i].delay) + fsleep(seq[i].delay); + } + + mutex_unlock(&spec->i2c_mux); + + return 0; + +error: + mutex_unlock(&spec->i2c_mux); + codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); + return -EIO; +} + +static int cs8409_init(struct hda_codec *codec) +{ + int ret = snd_hda_gen_init(codec); + + if (!ret) + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return ret; +} + +static int cs8409_build_controls(struct hda_codec *codec) +{ + int err; + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + + return 0; +} + +/* Enable/Disable Unsolicited Response */ +static void cs8409_enable_ur(struct hda_codec *codec, int flag) +{ + struct cs8409_spec *spec = codec->spec; + unsigned int ur_gpios = 0; + int i; + + for (i = 0; i < spec->num_scodecs; i++) + ur_gpios |= spec->scodecs[i]->irq_mask; + + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, + flag ? ur_gpios : 0); + + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE, + flag ? AC_UNSOL_ENABLED : 0); +} + +static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid) +{ + int caps; + + /* CS8409 is simple HDA bridge and intended to be used with a remote + * companion codec. Most of input/output PIN(s) have only basic + * capabilities. Receive and Transmit NID(s) have only OUTC and INC + * capabilities and no presence detect capable (PDC) and call to + * snd_hda_gen_build_controls() will mark them as non detectable + * phantom jacks. However, a companion codec may be + * connected to these pins which supports jack detect + * capabilities. We have to override pin capabilities, + * otherwise they will not be created as input devices. + */ + caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); + if (caps >= 0) + snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, + (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); + + snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); +} + +static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio); + return 0; +} + +static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + unsigned int gpio_data; + + gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) | + (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0); + if (gpio_data == spec->gpio_data) + return 0; + spec->gpio_data = gpio_data; + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); + return 1; +} + +static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ctl_boolean_mono_info, + .get = cs8409_spk_sw_gpio_get, + .put = cs8409_spk_sw_gpio_put, +}; + +/****************************************************************************** + * CS42L42 Specific Functions + ******************************************************************************/ + +int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo) +{ + unsigned int ofs = get_amp_offset(kctrl); + u8 chs = get_amp_channels(kctrl); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.step = 1; + uinfo->count = chs == 3 ? 2 : 1; + + switch (ofs) { + case CS42L42_VOL_DAC: + uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN; + uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX; + break; + case CS42L42_VOL_ADC: + uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN; + uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX; + break; + default: + break; + } + + return 0; +} + +int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) +{ + struct hda_codec *codec = snd_kcontrol_chip(kctrl); + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; + int chs = get_amp_channels(kctrl); + unsigned int ofs = get_amp_offset(kctrl); + long *valp = uctrl->value.integer.value; + + switch (ofs) { + case CS42L42_VOL_DAC: + if (chs & BIT(0)) + *valp++ = cs42l42->vol[ofs]; + if (chs & BIT(1)) + *valp = cs42l42->vol[ofs+1]; + break; + case CS42L42_VOL_ADC: + if (chs & BIT(0)) + *valp = cs42l42->vol[ofs]; + break; + default: + break; + } + + return 0; +} + +static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type, + unsigned int chs, bool mute) +{ + if (mute) { + if (vol_type == CS42L42_VOL_DAC) { + if (chs & BIT(0)) + cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f); + if (chs & BIT(1)) + cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f); + } else if (vol_type == CS42L42_VOL_ADC) { + if (chs & BIT(0)) + cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f); + } + } else { + if (vol_type == CS42L42_VOL_DAC) { + if (chs & BIT(0)) + cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, + -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET]) + & CS42L42_MIXER_CH_VOL_MASK); + if (chs & BIT(1)) + cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, + -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET]) + & CS42L42_MIXER_CH_VOL_MASK); + } else if (vol_type == CS42L42_VOL_ADC) { + if (chs & BIT(0)) + cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, + cs42l42->vol[CS42L42_ADC_VOL_OFFSET] + & CS42L42_REG_AMIC_VOL_MASK); + } + } +} + +int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) +{ + struct hda_codec *codec = snd_kcontrol_chip(kctrl); + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; + int chs = get_amp_channels(kctrl); + unsigned int ofs = get_amp_offset(kctrl); + long *valp = uctrl->value.integer.value; + + switch (ofs) { + case CS42L42_VOL_DAC: + if (chs & BIT(0)) + cs42l42->vol[ofs] = *valp; + if (chs & BIT(1)) { + valp++; + cs42l42->vol[ofs + 1] = *valp; + } + if (spec->playback_started) + cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false); + break; + case CS42L42_VOL_ADC: + if (chs & BIT(0)) + cs42l42->vol[ofs] = *valp; + if (spec->capture_started) + cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false); + break; + default: + break; + } + + return 0; +} + +static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42; + int i; + bool mute; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + mute = false; + spec->playback_started = 1; + break; + case HDA_GEN_PCM_ACT_CLEANUP: + mute = true; + spec->playback_started = 0; + break; + default: + return; + } + + for (i = 0; i < spec->num_scodecs; i++) { + cs42l42 = spec->scodecs[i]; + cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute); + } +} + +static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42; + int i; + bool mute; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + mute = false; + spec->capture_started = 1; + break; + case HDA_GEN_PCM_ACT_CLEANUP: + mute = true; + spec->capture_started = 0; + break; + default: + return; + } + + for (i = 0; i < spec->num_scodecs; i++) { + cs42l42 = spec->scodecs[i]; + cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute); + } +} + +/* Configure CS42L42 slave codec for jack autodetect */ +static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42) +{ + cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz); + /* Clear WAKE# */ + cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1); + /* Wait ~2.5ms */ + usleep_range(2500, 3000); + /* Set mode WAKE# output follows the combination logic directly */ + cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0); + /* Clear interrupts status */ + cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); + /* Enable interrupt */ + cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); +} + +/* Enable and run CS42L42 slave codec jack auto detect */ +static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) +{ + /* Clear interrupts */ + cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); + cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1); + cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF); + cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); + + cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87); + cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86); + cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07); + cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD); + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); + /* Wait ~20ms*/ + usleep_range(20000, 25000); + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77); + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0); +} + +static int cs42l42_manual_hs_det(struct sub_codec *cs42l42) +{ + unsigned int hs_det_status; + unsigned int hs_det_comp1; + unsigned int hs_det_comp2; + unsigned int hs_det_sw; + unsigned int hs_type; + + /* Set hs detect to manual, active mode */ + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, + (1 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, + (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); + + /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); + + msleep(100); + + hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); + + hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT; + hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT; + + /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); + + msleep(100); + + hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); + + hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; + hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; + + /* Use Comparator 1 with 1.25V Threshold. */ + switch (hs_det_comp1) { + case CS42L42_HSDET_COMP_TYPE1: + hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + default: + /* Fallback to Comparator 2 with 1.75V Threshold. */ + switch (hs_det_comp2) { + case CS42L42_HSDET_COMP_TYPE1: + hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + case CS42L42_HSDET_COMP_TYPE3: + hs_type = CS42L42_PLUG_HEADPHONE; + hs_det_sw = CS42L42_HSDET_SW_TYPE3; + break; + default: + hs_type = CS42L42_PLUG_INVALID; + hs_det_sw = CS42L42_HSDET_SW_TYPE4; + break; + } + } + + /* Set Switches */ + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw); + + /* Set HSDET mode to Manual—Disabled */ + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, + (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); + + return hs_type; +} + +static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) +{ + int status_changed = 0; + + /* TIP_SENSE INSERT/REMOVE */ + switch (reg_ts_status) { + case CS42L42_TS_PLUG: + if (cs42l42->no_type_dect) { + status_changed = 1; + cs42l42->hp_jack_in = 1; + cs42l42->mic_jack_in = 0; + } else { + cs42l42_run_jack_detect(cs42l42); + } + break; + + case CS42L42_TS_UNPLUG: + status_changed = 1; + cs42l42->hp_jack_in = 0; + cs42l42->mic_jack_in = 0; + break; + default: + /* jack in transition */ + break; + } + + codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status); + + return status_changed; +} + +static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42) +{ + int current_plug_status; + int status_changed = 0; + int reg_cdc_status; + int reg_hs_status; + int reg_ts_status; + int type; + + /* Read jack detect status registers */ + reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); + reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); + reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); + + /* If status values are < 0, read error has occurred. */ + if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0) + return -EIO; + + current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) + >> CS42L42_TS_PLUG_SHIFT; + + /* HSDET_AUTO_DONE */ + if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) { + + /* Disable HSDET_AUTO_DONE */ + cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF); + + type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT; + + /* Configure the HSDET mode. */ + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); + + if (cs42l42->no_type_dect) { + status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); + } else { + if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) { + codec_dbg(cs42l42->codec, + "Auto detect value not valid (%d), running manual det\n", + type); + type = cs42l42_manual_hs_det(cs42l42); + } + + switch (type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + status_changed = 1; + cs42l42->hp_jack_in = 1; + cs42l42->mic_jack_in = 1; + break; + case CS42L42_PLUG_HEADPHONE: + status_changed = 1; + cs42l42->hp_jack_in = 1; + cs42l42->mic_jack_in = 0; + break; + default: + status_changed = 1; + cs42l42->hp_jack_in = 0; + cs42l42->mic_jack_in = 0; + break; + } + codec_dbg(cs42l42->codec, "Detection done (%d)\n", type); + } + + /* Enable the HPOUT ground clamp and configure the HP pull-down */ + cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02); + /* Re-Enable Tip Sense Interrupt */ + cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); + } else { + status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); + } + + return status_changed; +} + +static void cs42l42_resume(struct sub_codec *cs42l42) +{ + struct hda_codec *codec = cs42l42->codec; + struct cs8409_spec *spec = codec->spec; + struct cs8409_i2c_param irq_regs[] = { + { CS42L42_CODEC_STATUS, 0x00 }, + { CS42L42_DET_INT_STATUS1, 0x00 }, + { CS42L42_DET_INT_STATUS2, 0x00 }, + { CS42L42_TSRS_PLUG_STATUS, 0x00 }, + }; + unsigned int fsv; + + /* Bring CS42L42 out of Reset */ + spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); + spec->gpio_data |= cs42l42->reset_gpio; + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); + usleep_range(10000, 15000); + + cs42l42->suspended = 0; + + /* Initialize CS42L42 companion codec */ + cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); + + /* Clear interrupts, by reading interrupt status registers */ + cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); + + fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); + if (cs42l42->full_scale_vol) { + // Set the full scale volume bit + fsv |= CS42L42_FULL_SCALE_VOL_MASK; + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); + } + // Unmute analog channels A and B + fsv = (fsv & ~CS42L42_ANA_MUTE_AB); + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); + + /* we have to explicitly allow unsol event handling even during the + * resume phase so that the jack event is processed properly + */ + snd_hda_codec_allow_unsol_events(cs42l42->codec); + + cs42l42_enable_jack_detect(cs42l42); +} + +static void cs42l42_suspend(struct sub_codec *cs42l42) +{ + struct hda_codec *codec = cs42l42->codec; + struct cs8409_spec *spec = codec->spec; + int reg_cdc_status = 0; + const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = { + { CS42L42_DAC_CTL2, 0x02 }, + { CS42L42_HS_CLAMP_DISABLE, 0x00 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_ASP_RX_DAI0_EN, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x00 }, + { CS42L42_PWR_CTL1, 0xFE }, + { CS42L42_PWR_CTL2, 0x8C }, + { CS42L42_PWR_CTL1, 0xFF }, + }; + + cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq)); + + if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status, + (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US, + true, cs42l42, CS42L42_CODEC_STATUS) < 0) + codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n"); + + /* Power down CS42L42 ASP/EQ/MIX/HP */ + cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C); + cs42l42->suspended = 1; + cs42l42->last_page = 0; + cs42l42->hp_jack_in = 0; + cs42l42->mic_jack_in = 0; + + /* Put CS42L42 into Reset */ + spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); + spec->gpio_data &= ~cs42l42->reset_gpio; + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); +} + +static void cs8409_free(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + /* Cancel i2c clock disable timer, and disable clock if left enabled */ + cancel_delayed_work_sync(&spec->i2c_clk_work); + cs8409_disable_i2c_clock(codec); + + snd_hda_gen_free(codec); +} + +/****************************************************************************** + * BULLSEYE / WARLOCK / CYBORG Specific Functions + * CS8409/CS42L42 + ******************************************************************************/ + +/* + * In the case of CS8409 we do not have unsolicited events from NID's 0x24 + * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will + * generate interrupt via gpio 4 to notify jack events. We have to overwrite + * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers + * and then notify status via generic snd_hda_jack_unsol_event() call. + */ +static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; + struct hda_jack_tbl *jk; + + /* jack_unsol_event() will be called every time gpio line changing state. + * In this case gpio4 line goes up as a result of reading interrupt status + * registers in previous cs8409_jack_unsol_event() call. + * We don't need to handle this event, ignoring... + */ + if (res & cs42l42->irq_mask) + return; + + if (cs42l42_jack_unsol_event(cs42l42)) { + snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, + cs42l42->hp_jack_in ? 0 : PIN_OUT); + /* Report jack*/ + jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); + if (jk) + snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & + AC_UNSOL_RES_TAG); + /* Report jack*/ + jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); + if (jk) + snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & + AC_UNSOL_RES_TAG); + } +} + +/* Manage PDREF, when transition to D3hot */ +static int cs8409_cs42l42_suspend(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + int i; + + spec->init_done = 0; + + cs8409_enable_ur(codec, 0); + + for (i = 0; i < spec->num_scodecs; i++) + cs42l42_suspend(spec->scodecs[i]); + + /* Cancel i2c clock disable timer, and disable clock if left enabled */ + cancel_delayed_work_sync(&spec->i2c_clk_work); + cs8409_disable_i2c_clock(codec); + + snd_hda_shutup_pins(codec); + + return 0; +} + +/* Vendor specific HW configuration + * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... + */ +static void cs8409_cs42l42_hw_init(struct hda_codec *codec) +{ + const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg; + const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn; + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + for (; seq->nid; seq++) + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); + + if (codec->fixup_id == CS8409_BULLSEYE) { + for (; seq_bullseye->nid; seq_bullseye++) + cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff); + } + + switch (codec->fixup_id) { + case CS8409_CYBORG: + case CS8409_WARLOCK_MLK_DUAL_MIC: + /* DMIC1_MO=00b, DMIC1/2_SR=1 */ + cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003); + break; + case CS8409_ODIN: + /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */ + cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00); + break; + default: + break; + } + + cs42l42_resume(cs42l42); + + /* Enable Unsolicited Response */ + cs8409_enable_ur(codec, 1); +} + +static const struct cs8409_variant_ops cs8409_cs42l42_vops = { + .build_controls = cs8409_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs8409_init, + .destroy = cs8409_free, + .unsol_event = cs8409_cs42l42_jack_unsol_event, + .suspend = cs8409_cs42l42_suspend, +}; + +static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ + switch (nid) { + case CS8409_CS42L42_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L42_AMIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } + + return spec->exec_verb(dev, cmd, flags, res); +} + +void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + struct cs8409_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs); + /* verb exec op override */ + spec->exec_verb = codec->core.exec_verb; + codec->core.exec_verb = cs8409_cs42l42_exec_verb; + + spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; + spec->num_scodecs = 1; + spec->scodecs[CS8409_CODEC0]->codec = codec; + spec->vops = &cs8409_cs42l42_vops; + + spec->gen.suppress_auto_mute = 1; + spec->gen.no_primary_hp = 1; + spec->gen.suppress_vmaster = 1; + + spec->speaker_pdn_gpio = 0; + + /* GPIO 5 out, 3,4 in */ + spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; + spec->gpio_data = 0; + spec->gpio_mask = 0x03f; + + /* Basic initial sequence for specific hw configuration */ + snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); + + cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID); + cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID); + + spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020; + + switch (codec->fixup_id) { + case CS8409_CYBORG: + spec->scodecs[CS8409_CODEC0]->full_scale_vol = + CS42L42_FULL_SCALE_VOL_MINUS6DB; + spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; + break; + case CS8409_ODIN: + spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; + spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; + break; + case CS8409_WARLOCK_MLK: + case CS8409_WARLOCK_MLK_DUAL_MIC: + spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; + spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; + break; + default: + spec->scodecs[CS8409_CODEC0]->full_scale_vol = + CS42L42_FULL_SCALE_VOL_MINUS6DB; + spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; + break; + } + + if (spec->speaker_pdn_gpio > 0) { + spec->gpio_dir |= spec->speaker_pdn_gpio; + spec->gpio_data |= spec->speaker_pdn_gpio; + } + + break; + case HDA_FIXUP_ACT_PROBE: + /* Fix Sample Rate to 48kHz */ + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; + spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; + /* add hooks */ + spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; + spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; + if (codec->fixup_id != CS8409_ODIN) + /* Set initial DMIC volume to -26 dB */ + snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, + HDA_INPUT, 0, 0xff, 0x19); + snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", + &cs42l42_dac_volume_mixer); + snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", + &cs42l42_adc_volume_mixer); + if (spec->speaker_pdn_gpio > 0) + snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", + &cs8409_spk_sw_ctrl); + /* Disable Unsolicited Response during boot */ + cs8409_enable_ur(codec, 0); + snd_hda_codec_set_name(codec, "CS8409/CS42L42"); + break; + case HDA_FIXUP_ACT_INIT: + cs8409_cs42l42_hw_init(codec); + spec->init_done = 1; + if (spec->init_done && spec->build_ctrl_done + && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) + cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); + break; + case HDA_FIXUP_ACT_BUILD: + spec->build_ctrl_done = 1; + /* Run jack auto detect first time on boot + * after controls have been added, to check if jack has + * been already plugged in. + * Run immediately after init. + */ + if (spec->init_done && spec->build_ctrl_done + && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) + cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); + break; + default: + break; + } +} + +/****************************************************************************** + * Dolphin Specific Functions + * CS8409/ 2 X CS42L42 + ******************************************************************************/ + +/* + * In the case of CS8409 we do not have unsolicited events when + * hs mic and hp are connected. Companion codec CS42L42 will + * generate interrupt via irq_mask to notify jack events. We have to overwrite + * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers + * and then notify status via generic snd_hda_jack_unsol_event() call. + */ +static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42; + struct hda_jack_tbl *jk; + + cs42l42 = spec->scodecs[CS8409_CODEC0]; + if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && + cs42l42_jack_unsol_event(cs42l42)) { + jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0); + if (jk) + snd_hda_jack_unsol_event(codec, + (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & + AC_UNSOL_RES_TAG); + + jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0); + if (jk) + snd_hda_jack_unsol_event(codec, + (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & + AC_UNSOL_RES_TAG); + } + + cs42l42 = spec->scodecs[CS8409_CODEC1]; + if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && + cs42l42_jack_unsol_event(cs42l42)) { + jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0); + if (jk) + snd_hda_jack_unsol_event(codec, + (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & + AC_UNSOL_RES_TAG); + } +} + +/* Vendor specific HW configuration + * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... + */ +static void dolphin_hw_init(struct hda_codec *codec) +{ + const struct cs8409_cir_param *seq = dolphin_hw_cfg; + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42; + int i; + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + for (; seq->nid; seq++) + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); + + for (i = 0; i < spec->num_scodecs; i++) { + cs42l42 = spec->scodecs[i]; + cs42l42_resume(cs42l42); + } + + /* Enable Unsolicited Response */ + cs8409_enable_ur(codec, 1); +} + +static const struct cs8409_variant_ops cs8409_dolphin_vops = { + .build_controls = cs8409_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs8409_init, + .destroy = cs8409_free, + .unsol_event = dolphin_jack_unsol_event, + .suspend = cs8409_cs42l42_suspend, +}; + +static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_spec *spec = codec->spec; + struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept calls for CS42L42 pins + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ + switch (nid) { + case DOLPHIN_HP_PIN_NID: + case DOLPHIN_LO_PIN_NID: + if (nid == DOLPHIN_LO_PIN_NID) + cs42l42 = spec->scodecs[CS8409_CODEC1]; + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case DOLPHIN_AMIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } + + return spec->exec_verb(dev, cmd, flags, res); +} + +void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + struct cs8409_spec *spec = codec->spec; + struct snd_kcontrol_new *kctrl; + int i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_add_verbs(codec, dolphin_init_verbs); + /* verb exec op override */ + spec->exec_verb = codec->core.exec_verb; + codec->core.exec_verb = dolphin_exec_verb; + + spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0; + spec->scodecs[CS8409_CODEC0]->codec = codec; + spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1; + spec->scodecs[CS8409_CODEC1]->codec = codec; + spec->num_scodecs = 2; + spec->gen.suppress_vmaster = 1; + + spec->vops = &cs8409_dolphin_vops; + + /* GPIO 1,5 out, 0,4 in */ + spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | + spec->scodecs[CS8409_CODEC1]->reset_gpio; + spec->gpio_data = 0; + spec->gpio_mask = 0x03f; + + /* Basic initial sequence for specific hw configuration */ + snd_hda_sequence_write(codec, dolphin_init_verbs); + + snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true, + SND_JACK_HEADPHONE, NULL); + + snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true, + SND_JACK_MICROPHONE, NULL); + + cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID); + cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID); + cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID); + + spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; + spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; + + break; + case HDA_FIXUP_ACT_PROBE: + /* Fix Sample Rate to 48kHz */ + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; + spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; + /* add hooks */ + spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; + spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; + snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", + &cs42l42_dac_volume_mixer); + snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer); + kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume", + &cs42l42_dac_volume_mixer); + /* Update Line Out kcontrol template */ + if (kctrl) + kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, + HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; + cs8409_enable_ur(codec, 0); + snd_hda_codec_set_name(codec, "CS8409/CS42L42"); + break; + case HDA_FIXUP_ACT_INIT: + dolphin_hw_init(codec); + spec->init_done = 1; + if (spec->init_done && spec->build_ctrl_done) { + for (i = 0; i < spec->num_scodecs; i++) { + if (!spec->scodecs[i]->hp_jack_in) + cs42l42_run_jack_detect(spec->scodecs[i]); + } + } + break; + case HDA_FIXUP_ACT_BUILD: + spec->build_ctrl_done = 1; + /* Run jack auto detect first time on boot + * after controls have been added, to check if jack has + * been already plugged in. + * Run immediately after init. + */ + if (spec->init_done && spec->build_ctrl_done) { + for (i = 0; i < spec->num_scodecs; i++) { + if (!spec->scodecs[i]->hp_jack_in) + cs42l42_run_jack_detect(spec->scodecs[i]); + } + } + break; + default: + break; + } +} + +static int patch_cs8409_apple(struct hda_codec *codec); + +static int patch_cs8409(struct hda_codec *codec) +{ + int err; + + if (!cs8409_alloc_spec(codec)) + return -ENOMEM; + + printk("snd_hda_intel: Primary patch_cs8409\n"); + + snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups); + + // this seems the easiest way to separate and jump into the code for handling Apple machines using the 8409 + // note now freeing the just allocated spec - this undos the delayed work as not using mutex yet + if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET) { + printk("snd_hda_intel: Primary patch_cs8409 NOT FOUND trying APPLE\n"); + cs8409_free(codec); + err = patch_cs8409_apple(codec); + return err; + } + + codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, + codec->bus->pci->subsystem_vendor, + codec->bus->pci->subsystem_device); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = cs8409_parse_auto_config(codec); + if (err < 0) { + cs8409_free(codec); + return err; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; +} + + +// for the moment split the new code into an include file + +#include "patch_cirrus_apple.h" + +static int cs8409_drv_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + (void)id; + return patch_cs8409(codec); +} + +static void cs8409_drv_remove(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec) + return; + if (spec->vops && spec->vops->destroy) + spec->vops->destroy(codec); + else + cs8409_free(codec); +} + +static int cs8409_drv_build_controls(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->build_controls) + return -EINVAL; + return spec->vops->build_controls(codec); +} + +static int cs8409_drv_build_pcms(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->build_pcms) + return -EINVAL; + return spec->vops->build_pcms(codec); +} + +static int cs8409_drv_init(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->init) + return 0; + return spec->vops->init(codec); +} + +static void cs8409_drv_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_spec *spec = codec->spec; + + if (spec && spec->vops && spec->vops->unsol_event) + spec->vops->unsol_event(codec, res); +} + +static int cs8409_drv_suspend(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->suspend) + return 0; + return spec->vops->suspend(codec); +} + +static int cs8409_drv_resume(struct hda_codec *codec) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->resume) + return 0; + return spec->vops->resume(codec); +} + +static int cs8409_drv_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct cs8409_spec *spec = codec->spec; + + if (!spec || !spec->vops || !spec->vops->check_power_status) + return 0; + return spec->vops->check_power_status(codec, nid); +} + +static const struct hda_codec_ops cs8409_driver_ops = { + .probe = cs8409_drv_probe, + .remove = cs8409_drv_remove, + .build_controls = cs8409_drv_build_controls, + .build_pcms = cs8409_drv_build_pcms, + .init = cs8409_drv_init, + .unsol_event = cs8409_drv_unsol_event, + .suspend = cs8409_drv_suspend, + .resume = cs8409_drv_resume, + .check_power_status = cs8409_drv_check_power_status, +}; + +static const struct hda_device_id snd_hda_id_cs8409[] = { + HDA_CODEC_ID_MODEL(0x10138409, "CS8409", 0), + {} +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); + +static struct hda_codec_driver cs8409_driver = { + .id = snd_hda_id_cs8409, + .ops = &cs8409_driver_ops, +}; +module_hda_codec_driver(cs8409_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); diff --git a/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h new file mode 100644 index 0000000..5e41c2a --- /dev/null +++ b/patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h @@ -0,0 +1,540 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __CS8409_PATCH_H +#define __CS8409_PATCH_H + +/* + * Apple-specific fields and patch_cirrus_apple.h require APPLE_CODECS. The local + * Makefile passes -DAPPLE_CODECS via CFLAGS_MODULE; default here so a plain + * "make -C .../build M=$(pwd) modules" without those flags still compiles. + */ +#ifndef APPLE_CODECS +#define APPLE_CODECS 1 +#endif + +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" + +/* CS8409 Specific Definitions */ + +enum cs8409_pins { + CS8409_PIN_ROOT, + CS8409_PIN_AFG, + CS8409_PIN_ASP1_OUT_A, + CS8409_PIN_ASP1_OUT_B, + CS8409_PIN_ASP1_OUT_C, + CS8409_PIN_ASP1_OUT_D, + CS8409_PIN_ASP1_OUT_E, + CS8409_PIN_ASP1_OUT_F, + CS8409_PIN_ASP1_OUT_G, + CS8409_PIN_ASP1_OUT_H, + CS8409_PIN_ASP2_OUT_A, + CS8409_PIN_ASP2_OUT_B, + CS8409_PIN_ASP2_OUT_C, + CS8409_PIN_ASP2_OUT_D, + CS8409_PIN_ASP2_OUT_E, + CS8409_PIN_ASP2_OUT_F, + CS8409_PIN_ASP2_OUT_G, + CS8409_PIN_ASP2_OUT_H, + CS8409_PIN_ASP1_IN_A, + CS8409_PIN_ASP1_IN_B, + CS8409_PIN_ASP1_IN_C, + CS8409_PIN_ASP1_IN_D, + CS8409_PIN_ASP1_IN_E, + CS8409_PIN_ASP1_IN_F, + CS8409_PIN_ASP1_IN_G, + CS8409_PIN_ASP1_IN_H, + CS8409_PIN_ASP2_IN_A, + CS8409_PIN_ASP2_IN_B, + CS8409_PIN_ASP2_IN_C, + CS8409_PIN_ASP2_IN_D, + CS8409_PIN_ASP2_IN_E, + CS8409_PIN_ASP2_IN_F, + CS8409_PIN_ASP2_IN_G, + CS8409_PIN_ASP2_IN_H, + CS8409_PIN_DMIC1, + CS8409_PIN_DMIC2, + CS8409_PIN_ASP1_TRANSMITTER_A, + CS8409_PIN_ASP1_TRANSMITTER_B, + CS8409_PIN_ASP1_TRANSMITTER_C, + CS8409_PIN_ASP1_TRANSMITTER_D, + CS8409_PIN_ASP1_TRANSMITTER_E, + CS8409_PIN_ASP1_TRANSMITTER_F, + CS8409_PIN_ASP1_TRANSMITTER_G, + CS8409_PIN_ASP1_TRANSMITTER_H, + CS8409_PIN_ASP2_TRANSMITTER_A, + CS8409_PIN_ASP2_TRANSMITTER_B, + CS8409_PIN_ASP2_TRANSMITTER_C, + CS8409_PIN_ASP2_TRANSMITTER_D, + CS8409_PIN_ASP2_TRANSMITTER_E, + CS8409_PIN_ASP2_TRANSMITTER_F, + CS8409_PIN_ASP2_TRANSMITTER_G, + CS8409_PIN_ASP2_TRANSMITTER_H, + CS8409_PIN_ASP1_RECEIVER_A, + CS8409_PIN_ASP1_RECEIVER_B, + CS8409_PIN_ASP1_RECEIVER_C, + CS8409_PIN_ASP1_RECEIVER_D, + CS8409_PIN_ASP1_RECEIVER_E, + CS8409_PIN_ASP1_RECEIVER_F, + CS8409_PIN_ASP1_RECEIVER_G, + CS8409_PIN_ASP1_RECEIVER_H, + CS8409_PIN_ASP2_RECEIVER_A, + CS8409_PIN_ASP2_RECEIVER_B, + CS8409_PIN_ASP2_RECEIVER_C, + CS8409_PIN_ASP2_RECEIVER_D, + CS8409_PIN_ASP2_RECEIVER_E, + CS8409_PIN_ASP2_RECEIVER_F, + CS8409_PIN_ASP2_RECEIVER_G, + CS8409_PIN_ASP2_RECEIVER_H, + CS8409_PIN_DMIC1_IN, + CS8409_PIN_DMIC2_IN, + CS8409_PIN_BEEP_GEN, + CS8409_PIN_VENDOR_WIDGET +}; + +enum cs8409_coefficient_index_registers { + CS8409_DEV_CFG1, + CS8409_DEV_CFG2, + CS8409_DEV_CFG3, + CS8409_ASP1_CLK_CTRL1, + CS8409_ASP1_CLK_CTRL2, + CS8409_ASP1_CLK_CTRL3, + CS8409_ASP2_CLK_CTRL1, + CS8409_ASP2_CLK_CTRL2, + CS8409_ASP2_CLK_CTRL3, + CS8409_DMIC_CFG, + CS8409_BEEP_CFG, + ASP1_RX_NULL_INS_RMV, + ASP1_Rx_RATE1, + ASP1_Rx_RATE2, + ASP1_Tx_NULL_INS_RMV, + ASP1_Tx_RATE1, + ASP1_Tx_RATE2, + ASP2_Rx_NULL_INS_RMV, + ASP2_Rx_RATE1, + ASP2_Rx_RATE2, + ASP2_Tx_NULL_INS_RMV, + ASP2_Tx_RATE1, + ASP2_Tx_RATE2, + ASP1_SYNC_CTRL, + ASP2_SYNC_CTRL, + ASP1_A_TX_CTRL1, + ASP1_A_TX_CTRL2, + ASP1_B_TX_CTRL1, + ASP1_B_TX_CTRL2, + ASP1_C_TX_CTRL1, + ASP1_C_TX_CTRL2, + ASP1_D_TX_CTRL1, + ASP1_D_TX_CTRL2, + ASP1_E_TX_CTRL1, + ASP1_E_TX_CTRL2, + ASP1_F_TX_CTRL1, + ASP1_F_TX_CTRL2, + ASP1_G_TX_CTRL1, + ASP1_G_TX_CTRL2, + ASP1_H_TX_CTRL1, + ASP1_H_TX_CTRL2, + ASP2_A_TX_CTRL1, + ASP2_A_TX_CTRL2, + ASP2_B_TX_CTRL1, + ASP2_B_TX_CTRL2, + ASP2_C_TX_CTRL1, + ASP2_C_TX_CTRL2, + ASP2_D_TX_CTRL1, + ASP2_D_TX_CTRL2, + ASP2_E_TX_CTRL1, + ASP2_E_TX_CTRL2, + ASP2_F_TX_CTRL1, + ASP2_F_TX_CTRL2, + ASP2_G_TX_CTRL1, + ASP2_G_TX_CTRL2, + ASP2_H_TX_CTRL1, + ASP2_H_TX_CTRL2, + ASP1_A_RX_CTRL1, + ASP1_A_RX_CTRL2, + ASP1_B_RX_CTRL1, + ASP1_B_RX_CTRL2, + ASP1_C_RX_CTRL1, + ASP1_C_RX_CTRL2, + ASP1_D_RX_CTRL1, + ASP1_D_RX_CTRL2, + ASP1_E_RX_CTRL1, + ASP1_E_RX_CTRL2, + ASP1_F_RX_CTRL1, + ASP1_F_RX_CTRL2, + ASP1_G_RX_CTRL1, + ASP1_G_RX_CTRL2, + ASP1_H_RX_CTRL1, + ASP1_H_RX_CTRL2, + ASP2_A_RX_CTRL1, + ASP2_A_RX_CTRL2, + ASP2_B_RX_CTRL1, + ASP2_B_RX_CTRL2, + ASP2_C_RX_CTRL1, + ASP2_C_RX_CTRL2, + ASP2_D_RX_CTRL1, + ASP2_D_RX_CTRL2, + ASP2_E_RX_CTRL1, + ASP2_E_RX_CTRL2, + ASP2_F_RX_CTRL1, + ASP2_F_RX_CTRL2, + ASP2_G_RX_CTRL1, + ASP2_G_RX_CTRL2, + ASP2_H_RX_CTRL1, + ASP2_H_RX_CTRL2, + CS8409_I2C_ADDR, + CS8409_I2C_DATA, + CS8409_I2C_CTRL, + CS8409_I2C_STS, + CS8409_I2C_QWRITE, + CS8409_I2C_QREAD, + CS8409_SPI_CTRL, + CS8409_SPI_TX_DATA, + CS8409_SPI_RX_DATA, + CS8409_SPI_STS, + CS8409_PFE_COEF_W1, /* Parametric filter engine coefficient write 1*/ + CS8409_PFE_COEF_W2, + CS8409_PFE_CTRL1, + CS8409_PFE_CTRL2, + CS8409_PRE_SCALE_ATTN1, + CS8409_PRE_SCALE_ATTN2, + CS8409_PFE_COEF_MON1, /* Parametric filter engine coefficient monitor 1*/ + CS8409_PFE_COEF_MON2, + CS8409_ASP1_INTRN_STS, + CS8409_ASP2_INTRN_STS, + CS8409_ASP1_RX_SCLK_COUNT, + CS8409_ASP1_TX_SCLK_COUNT, + CS8409_ASP2_RX_SCLK_COUNT, + CS8409_ASP2_TX_SCLK_COUNT, + CS8409_ASP_UNS_RESP_MASK, + CS8409_LOOPBACK_CTRL = 0x80, + CS8409_PAD_CFG_SLW_RATE_CTRL = 0x82, /* Pad Config and Slew Rate Control (CIR = 0x0082) */ +}; + +/* CS42L42 Specific Definitions */ + +#define CS8409_MAX_CODECS 8 +#define CS42L42_VOLUMES (4U) +#define CS42L42_HP_VOL_REAL_MIN (-63) +#define CS42L42_HP_VOL_REAL_MAX (0) +#define CS42L42_AMIC_VOL_REAL_MIN (-97) +#define CS42L42_AMIC_VOL_REAL_MAX (12) +#define CS42L42_REG_AMIC_VOL_MASK (0x00FF) +#define CS42L42_HSTYPE_MASK (0x03) +#define CS42L42_I2C_TIMEOUT_US (20000) +#define CS42L42_I2C_SLEEP_US (2000) +#define CS42L42_PDN_TIMEOUT_US (250000) +#define CS42L42_PDN_SLEEP_US (2000) +#define CS42L42_ANA_MUTE_AB (0x0C) +#define CS42L42_FULL_SCALE_VOL_MASK (2) +#define CS42L42_FULL_SCALE_VOL_0DB (0) +#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1) + +/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */ + +#define CS42L42_I2C_ADDR (0x48 << 1) +#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */ +#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */ +#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */ +#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */ +#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A +#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A +#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A +#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN +#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1 + +/* Dolphin */ + +#define DOLPHIN_C0_I2C_ADDR (0x48 << 1) +#define DOLPHIN_C1_I2C_ADDR (0x49 << 1) +#define DOLPHIN_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A +#define DOLPHIN_LO_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_B +#define DOLPHIN_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A + +#define DOLPHIN_C0_INT GENMASK(4, 4) +#define DOLPHIN_C1_INT GENMASK(0, 0) +#define DOLPHIN_C0_RESET GENMASK(5, 5) +#define DOLPHIN_C1_RESET GENMASK(1, 1) +#define DOLPHIN_WAKE (DOLPHIN_C0_INT | DOLPHIN_C1_INT) + +enum { + CS8409_BULLSEYE, + CS8409_WARLOCK, + CS8409_WARLOCK_MLK, + CS8409_WARLOCK_MLK_DUAL_MIC, + CS8409_CYBORG, + CS8409_FIXUPS, + CS8409_DOLPHIN, + CS8409_DOLPHIN_FIXUPS, + CS8409_ODIN, +}; + +enum { + CS8409_CODEC0, + CS8409_CODEC1 +}; + +enum { + CS42L42_VOL_ADC, + CS42L42_VOL_DAC, +}; + +#define CS42L42_ADC_VOL_OFFSET (CS42L42_VOL_ADC) +#define CS42L42_DAC_CH0_VOL_OFFSET (CS42L42_VOL_DAC) +#define CS42L42_DAC_CH1_VOL_OFFSET (CS42L42_VOL_DAC + 1) + +struct cs8409_i2c_param { + unsigned int addr; + unsigned int value; + unsigned int delay; +}; + +struct cs8409_cir_param { + unsigned int nid; + unsigned int cir; + unsigned int coeff; +}; + + +#ifdef APPLE_CODECS +struct unsol_item { + struct list_head list; + unsigned int idx; + unsigned int res; +}; +struct hda_cvt_setup_apple { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; +#endif + +struct sub_codec { + struct hda_codec *codec; + unsigned int addr; + unsigned int reset_gpio; + unsigned int irq_mask; + const struct cs8409_i2c_param *init_seq; + unsigned int init_seq_num; + + unsigned int hp_jack_in:1; + unsigned int mic_jack_in:1; +#ifdef APPLE_CODECS + unsigned int linein_jack_in:1; +#endif + unsigned int suspended:1; + unsigned int paged:1; + unsigned int last_page; + unsigned int hsbias_hiz; + unsigned int full_scale_vol:1; + unsigned int no_type_dect:1; + + s8 vol[CS42L42_VOLUMES]; +}; + +/* + * Per-machine callbacks; older kernels stored these in codec->patch_ops (removed + * in favor of struct hda_codec_driver + ops->probe/remove/...). + */ +struct cs8409_variant_ops { + int (*build_controls)(struct hda_codec *codec); + int (*build_pcms)(struct hda_codec *codec); + int (*init)(struct hda_codec *codec); + void (*unsol_event)(struct hda_codec *codec, unsigned int res); + int (*suspend)(struct hda_codec *codec); + int (*resume)(struct hda_codec *codec); + int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); + void (*destroy)(struct hda_codec *codec); +}; + +struct cs8409_spec { + struct hda_gen_spec gen; + struct hda_codec *codec; + const struct cs8409_variant_ops *vops; + + struct sub_codec *scodecs[CS8409_MAX_CODECS]; + unsigned int num_scodecs; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + + int speaker_pdn_gpio; + + struct mutex i2c_mux; + unsigned int i2c_clck_enabled; + unsigned int dev_addr; + struct delayed_work i2c_clk_work; + + unsigned int playback_started:1; + unsigned int capture_started:1; + unsigned int init_done:1; + unsigned int build_ctrl_done:1; + +#ifdef APPLE_CODECS + + // additional data for Apple 8409 system + + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* digital beep */ + hda_nid_t beep_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + + // so it appears we have "concurrency" in the linux HDA code + // in that if unsolicited responses occur which perform extensive verbs + // the hda verbs are intermixed with eg extensive start playback verbs + // on OSX we appear to have blocks of verbs during which unsolicited responses + // are logged but the unsolicited verbs occur after the verb block + // this flag is used to flag such verb blocks and the list will store the + // responses + // we use a pre-allocated list - if we have more than 10 outstanding unsols + // we will drop + // not clear if mutexes would be the way to go + int block_unsol; + struct list_head unsol_list; + struct unsol_item unsol_items_prealloc[10]; + int unsol_items_prealloc_used[10]; + + // add in specific nids for the intmike and linein as they seem to swap + // between macbook pros (14,3) and imacs (18,3) + int intmike_nid; + int linein_nid; + int intmike_adc_nid; + int linein_amp_nid; + + // the following flag bits also need swapping + int reg9_intmike_dmic_mo; + int reg9_linein_dmic_mo; + int reg82_intmike_dmic_scl; + int reg82_linein_dmic_scl; + + + // add explicit stream format store entries as per hda_codec using a local definition + // of hda_cvt_setup (which is local to hda_codec.c) + // also use explicit nid versions + // (except that means either need explicit functions for each nid or have to lookup + // nid each time want to use in a generic function with nid argument) + struct hda_cvt_setup_apple nid_0x02; + struct hda_cvt_setup_apple nid_0x03; + struct hda_cvt_setup_apple nid_0x0a; + struct hda_cvt_setup_apple nid_0x22; + struct hda_cvt_setup_apple nid_0x23; + struct hda_cvt_setup_apple nid_0x1a; + + + // new item to deal with jack presence as Apple (and now Dell) seems to have barfed + // the HDA spec by using a separate headphone chip + int jack_present; + + // save the type of headphone connected + int headset_type; + + // if headphone has mike or not + int have_mike; + + // if headphone has buttons or not + int have_buttons; + + // current stream channel count + int stream_channels; + + // set when playing for plug/unplug events while playing + int playing; + + // set when capturing for plug/unplug events while capturing + int capturing; + + // changing coding - OSX sets up the format on plugin + // then does some minimal setup when start play + // initial coding delayed any format setup till actually play + // this works for no mike but not for mike - we need to initialize + // the mike on plugin + // this flag will be set when we have done the format setup + // so know if need to do it on play or not + // now need 2 flags - one for play and one for capture + int headset_play_format_setup_needed; + int headset_capture_format_setup_needed; + + int headset_presetup_done; + + + int use_data; + + + // this is new item for dealing with headset plugins + // so can distinguish which phase we are in if have multiple interrupts + // not really used now have analyzed interrupts properly + int headset_phase; + + // another dirty hack item to manage the different headset enable codes + int headset_enable; + + int play_init; + int capture_init; + + + // new item to limit times we redo unmute/play + struct timespec64 last_play_time; + // record the first play time - we have a problem there + // some initial plays that I dont understand - so skip any setup + // till sometime after the first play + struct timespec64 first_play_time; + + +#endif + + + /* verb exec op override */ + int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res); +}; + +extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; +extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer; + +int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo); +int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl); +int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl); + +extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback; +extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture; +extern const struct hda_quirk cs8409_fixup_tbl[]; +extern const struct hda_model_fixup cs8409_models[]; +extern const struct hda_fixup cs8409_fixups[]; +extern const struct hda_verb cs8409_cs42l42_init_verbs[]; +extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[]; +extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[]; +extern struct sub_codec cs8409_cs42l42_codec; + +extern const struct hda_verb dolphin_init_verbs[]; +extern const struct cs8409_cir_param dolphin_hw_cfg[]; +extern struct sub_codec dolphin_cs42l42_0; +extern struct sub_codec dolphin_cs42l42_1; + +void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); +void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); + +#endif