Driver
This commit is contained in:
parent
477793d581
commit
c51070b7a8
25 changed files with 20640 additions and 3 deletions
|
|
@ -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/ /
|
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
|
# System-level units are enabled explicitly. User units are picked up
|
||||||
# automatically via overlay/usr/lib/systemd/user-preset/.
|
# 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
|
||||||
|
|
|
||||||
38
README.md
38
README.md
|
|
@ -5,7 +5,27 @@ Quine distro of Linux based on uBlue Aurora
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
`Containerfile` builds a custom bootc image on top of `ghcr.io/ublue-os/aurora:stable`,
|
`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
|
```sh
|
||||||
podman build -t quine-linux -f Containerfile .
|
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
|
`flatpak uninstall --unused -y`, clears `~/.cache`. Auto-enabled for every
|
||||||
user via `overlay/usr/lib/systemd/user-preset/90-quine-linux.preset` — no
|
user via `overlay/usr/lib/systemd/user-preset/90-quine-linux.preset` — no
|
||||||
manual `systemctl --user enable` needed.
|
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.
|
||||||
|
|
|
||||||
0
RUNME_PERIODICALLY.sh
Normal file → Executable file
0
RUNME_PERIODICALLY.sh
Normal file → Executable file
77
Taskfile.yml
Normal file
77
Taskfile.yml
Normal file
|
|
@ -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
|
||||||
235
docs/imac19-2-mic-fix.md
Normal file
235
docs/imac19-2-mic-fix.md
Normal file
|
|
@ -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 <mic source>` / `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/<kernel-version>/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)
|
||||||
|
|
@ -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
|
||||||
30
overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh
Executable file
30
overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh
Executable file
|
|
@ -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
|
||||||
|
|
@ -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)
|
||||||
|
{
|
||||||
60
patches/snd-hda-codec-cs8409/PROVENANCE.md
Normal file
60
patches/snd-hda-codec-cs8409/PROVENANCE.md
Normal file
|
|
@ -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.
|
||||||
8
patches/snd-hda-codec-cs8409/vendor/.gitignore
vendored
Normal file
8
patches/snd-hda-codec-cs8409/vendor/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
*.cmd
|
||||||
|
*.ko
|
||||||
|
*.o
|
||||||
|
Module.symvers
|
||||||
|
modules.order
|
||||||
|
*.mod
|
||||||
|
*.mod.c
|
||||||
|
.DS_Store
|
||||||
29
patches/snd-hda-codec-cs8409/vendor/Makefile
vendored
Normal file
29
patches/snd-hda-codec-cs8409/vendor/Makefile
vendored
Normal file
|
|
@ -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
|
||||||
92
patches/snd-hda-codec-cs8409/vendor/README.md
vendored
Normal file
92
patches/snd-hda-codec-cs8409/vendor/README.md
vendored
Normal file
|
|
@ -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.
|
||||||
118
patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h
vendored
Normal file
118
patches/snd-hda-codec-cs8409/vendor/hda_auto_parser.h
vendored
Normal file
|
|
@ -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 <tiwai@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 */
|
||||||
366
patches/snd-hda-codec-cs8409/vendor/hda_generic.h
vendored
Normal file
366
patches/snd-hda-codec-cs8409/vendor/hda_generic.h
vendored
Normal file
|
|
@ -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 <tiwai@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SOUND_HDA_GENERIC_H
|
||||||
|
#define __SOUND_HDA_GENERIC_H
|
||||||
|
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#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 */
|
||||||
195
patches/snd-hda-codec-cs8409/vendor/hda_jack.h
vendored
Normal file
195
patches/snd-hda-codec-cs8409/vendor/hda_jack.h
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
/*
|
||||||
|
* Jack-detection handling for HD-audio
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SOUND_HDA_JACK_H
|
||||||
|
#define __SOUND_HDA_JACK_H
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <sound/jack.h>
|
||||||
|
|
||||||
|
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 */
|
||||||
763
patches/snd-hda-codec-cs8409/vendor/hda_local.h
vendored
Normal file
763
patches/snd-hda-codec-cs8409/vendor/hda_local.h
vendored
Normal file
|
|
@ -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 <tiwai@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 */
|
||||||
3085
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h
vendored
Normal file
3085
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_apple.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
2452
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h
vendored
Normal file
2452
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_boot84.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
432
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h
vendored
Normal file
432
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_hda_generic_copy.h
vendored
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
1906
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h
vendored
Normal file
1906
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_new84.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
5954
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h
vendored
Normal file
5954
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1955
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h
vendored
Normal file
1955
patches/snd-hda-codec-cs8409/vendor/patch_cirrus_real84_i2c.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
623
patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c
vendored
Normal file
623
patches/snd-hda-codec-cs8409/vendor/patch_cs8409-tables.c
vendored
Normal file
|
|
@ -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 <tanureal@opensource.cirrus.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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,
|
||||||
|
},
|
||||||
|
};
|
||||||
1595
patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c
vendored
Normal file
1595
patches/snd-hda-codec-cs8409/vendor/patch_cs8409.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
540
patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h
vendored
Normal file
540
patches/snd-hda-codec-cs8409/vendor/patch_cs8409.h
vendored
Normal file
|
|
@ -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 <linux/pci.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <sound/cs42l42.h>
|
||||||
|
#include <sound/hda_codec.h>
|
||||||
|
#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
|
||||||
Loading…
Reference in a new issue