Skip to content

drm: apple: Adaptive Sync/ProMotion#477

Open
chadmed wants to merge 3348 commits into
AsahiLinux:asahi-wipfrom
chadmed:dcp/vrr
Open

drm: apple: Adaptive Sync/ProMotion#477
chadmed wants to merge 3348 commits into
AsahiLinux:asahi-wipfrom
chadmed:dcp/vrr

Conversation

@chadmed

@chadmed chadmed commented Apr 5, 2026

Copy link
Copy Markdown
Member

Tested:

  • Adaptive Sync over DisplayPort (Dell S2721DGF, [48, 165] Hz)
  • Apple ProMotion on 14" and 16" MacBook Pros (tested [24, 120] Hz)

Not tested:

  • Adaptive Sync over HDMI (I have no HDMI 2.x displays)

What works:

  • "Modesetting" in and out of Adaptive Sync
  • mpv... mostly
  • Games running via Proton via Steam via FEX via muvm
  • Native AArch64 games
  • Forcing Adaptive Sync on and sitting at the desktop at minimum refresh rate

What doesn't

  • Firefox will always render at the display's refresh rate
  • On the internal MBP displays only, mpv's gpu and gpu-next outputs leak file descriptors and halt the GPU when in fullscreen. Other backends work fine, other applications work fine, gpu and gpu-next work fine when not in fullscreen...

What needs work

  • The VRR timestamps are best guesses only. We know they are always within a few frames of each other, meaning that they are likely something to do with swap submission and presentation time, but what exactly remains unclear.
  • Wrapping set_digital_out_mode in a dcpep callback seems kind of silly? There's probably a smarter way to do this.

@jannau jannau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you verify this does anything at all on MacBook Pro internal displays? I would expect that this does nothing without EDID with adaptive sync data.

Comment thread drivers/gpu/drm/apple/parser.c
Comment thread drivers/gpu/drm/apple/parser.h Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.h Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
@chadmed chadmed force-pushed the dcp/vrr branch 2 times, most recently from 585c039 to ae20e32 Compare April 6, 2026 06:22
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
@chadmed chadmed force-pushed the dcp/vrr branch 2 times, most recently from 2e41bc2 to 31bf70d Compare April 7, 2026 12:14
Comment thread drivers/gpu/drm/apple/dcp.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/dcp.c Outdated
Comment thread drivers/gpu/drm/apple/dcp.c Outdated
Comment thread drivers/gpu/drm/apple/iomfb_template.c Outdated
Comment thread drivers/gpu/drm/apple/parser.h Outdated
kuba-moo and others added 13 commits June 1, 2026 17:54
[ Upstream commit 8054f85 ]

genlmsg_new() alloc failure path in net_shaper_nl_group_doit() forgets
to set ret before jumping to error handling.

Fixes: 5d5d470 ("net-shapers: implement NL group operation")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 0f9a857 ]

net_shaper_group_send_reply() writes both the NET_SHAPER_A_IFINDEX
attribute (via net_shaper_fill_binding()) and the nested
NET_SHAPER_A_HANDLE attribute (via net_shaper_fill_handle()), but
the reply skb at the call site in net_shaper_nl_group_doit() is
allocated using net_shaper_handle_size(), which only accounts for
the nested handle.

The allocation is therefore short by nla_total_size(sizeof(u32))
(8 bytes) for the IFINDEX attribute.  In practice the slab allocator
rounds up the small allocation so the bug is latent, but the size
accounting is wrong and could bite if the reply grew further.

Introduce net_shaper_group_reply_size() that accounts for the full
reply payload and use it both at the genlmsg_new() call site and in
the defensive WARN_ONCE message.

Fixes: 5d5d470 ("net-shapers: implement NL group operation")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit b62b29e ]

The NETDEV scope represents a singleton root shaper in the per-device
hierarchy.  All code assumes NETDEV shapers have id 0:
net_shaper_default_parent() hardcodes parent->id = 0 when returning
the NETDEV parent for QUEUE/NODE children, and the UAPI documentation
describes NETDEV scope as "the main shaper" (singular, not plural).

Make sure we reject non-0 IDs.

Fixes: 4b623f9 ("net-shapers: implement NL get operation")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit ce372e8 ]

net_shaper_parse_handle() does not enforce that the user provides
the handle ID. For NODE the ID defaults to UNSPEC for all other
cases it defaults to 0.

For NETDEV 0 is the only option. For QUEUE defaulting to 0 makes
less intuitive sense. Specifically because the behavior should
(IMHO) be the same for all cases where there may be more than
one ID (QUEUE and NODE).

We should either document this as intentional or reject.
I picked the latter with no strong conviction.

Fixes: 4b623f9 ("net-shapers: implement NL get operation")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 637ad3a ]

bio_integrity_add_page() already sets bip_vcnt to 1 for the bounce
segment. Overwriting it with nr_vecs breaks bip_vcnt <= bip_max_vcnt
on WRITE (bip_max_vcnt is 1), so the gap-merge checks in block/blk.h
read past the bip_vec[] flex array. On READ the read is in bounds
but lands on a saved user bvec instead of the bounce.

The line was added for split propagation, but bio_integrity_clone()
doesn't copy bip_vcnt and BIP_CLONE_FLAGS excludes BIP_COPY_USER.

Fixes: 3991657 ("block: set bip_vcnt correctly")
Signed-off-by: David Carlier <devnexen@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 2c6e6a1 ]

blk_insert_cloned_request() already recomputes nr_phys_segments
against the bottom queue, because "the queue settings related to
segment counting may differ from the original queue." The exact same
reasoning applies to integrity segments: a stacked driver's underlying
queue can have tighter virt_boundary_mask, seg_boundary_mask, or
max_segment_size than the top queue, in which case
blk_rq_count_integrity_sg() against the bottom queue produces a
different count than the cached rq->nr_integrity_segments inherited
from the source request by blk_rq_prep_clone().

When the cached count is lower than the bottom queue's actual count,
blk_rq_map_integrity_sg() trips

	BUG_ON(segments > rq->nr_integrity_segments);

on dispatch. The same families of stacked setups that motivated the
existing nr_phys_segments recompute -- dm-multipath fanning out to
nvme-rdma in particular -- can produce this.

Mirror the nr_phys_segments handling: when the request carries
integrity, recompute nr_integrity_segments against the bottom queue
and reject the request if it exceeds the bottom queue's
max_integrity_segments. blk_rq_count_integrity_sg() and
queue_max_integrity_segments() are both already available via
<linux/blk-integrity.h>, which blk-mq.c includes.

This closes a latent gap in the stacking contract and brings the
integrity-segment accounting in line with the existing
phys-segment accounting.

Fixes: 76c313f ("blk-integrity: improved sg segment mapping")
Signed-off-by: Casey Chen <cachen@purestorage.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 5f90dcf ]

Commit c7fabe4 ("HID: quirks: work around VID/PID conflict for
appledisplay") intends to add a quirk for kernels built with Apple Cinema
Display support, but it refers to the non-existing config option
CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display
support is named CONFIG_USB_APPLEDISPLAY.

Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef
directive.

Fixes: c7fabe4 ("HID: quirks: work around VID/PID conflict for appledisplay")
Signed-off-by: Lukas Bulwahn <lukas.bulwahn@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 8582792 ]

pin_user_pages_fast() can partially succeed and return the number of
pages that were actually pinned. However, the bio_integrity_map_user()
does not handle this partial pinning. This leads to a general protection
fault since bvec_from_pages() dereferences an unpinned page address,
which is 0.

To fix this, add a check to verify that all requested memory is pinned.
If partial pinning occurs, unpin the memory and return -EFAULT.

Kernel Oops:

Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty AsahiLinux#16 PREEMPT(lazy)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6

Fixes: 492c5d4 ("block: bio-integrity: directly map user buffers")
Acked-by: Chao Shi <cshi008@fiu.edu>
Acked-by: Weidong Zhu <weizhu@fiu.edu>
Acked-by: Dave Tian <daveti@purdue.edu>
Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Link: linux-blktests/blktests#244
Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit aa16b2b ]

The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to
(re)mapping beyond the VMA if the BO is too large. This can cause use
after free issues when munmap() unmaps only the VMA region and not the
additional mappings. To prevent this, check the remaining size of the
VMA before remapping and truncate the remapped length if sg->length is
too large.

Reported-by: Lukas Maar <lukas.maar@tugraz.at>
Fixes: ff13be8 ("accel/qaic: Add datapath")
Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
[jhugo: fix braces from checkpatch --strict]
Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 7bf563b ]

The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and
smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk:

	__string(name, smc->conn.lnk->ibname)

conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on
these paths already handles this (e.g. !conn->lnk in
SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first
sendmsg()/recvmsg() on an SMC-D socket crashes:

  Oops: general protection fault, probably for non-canonical address
  KASAN: null-ptr-deref in range [...]
  RIP: 0010:strlen+0x1e/0xa0
  Call Trace:
   trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44)
   smc_rx_recvmsg (net/smc/smc_rx.c:515)
   smc_recvmsg (net/smc/af_smc.c:2859)
   __sys_recvfrom (net/socket.c:2315)
   __x64_sys_recvfrom (net/socket.c:2326)
   do_syscall_64

The faulting address 0x3e0 is offsetof(struct smc_link, ibname),
confirming the NULL ->lnk deref. Enabling the tracepoint requires
root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has
no capability check, and SMC-D negotiation needs no admin step on
s390 or on x86 with the loopback ISM device loaded.

Log an empty device name for SMC-D instead of dereferencing NULL.

Fixes: aff3083 ("net/smc: Introduce tracepoints for tx and rx msg")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 3d04259 ]

ethnl_bitmap32_not_zero() should return true if some bit in [start, end)
is set:

- Fix inverted memchr_inv() sense: return true when the scan finds a
  non-zero byte, not when the middle words are all zero.
- Return false for an empty interval (end <= start).
- When end is 32-bit aligned, indices in [start, end) do not include any
  bits from map[end_word]; return false after earlier checks found no
  non-zero data.

Fixes: 10b518d ("ethtool: netlink bitset handling")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit 933430f ]

The UV scanlines is calculated with (height + 1) / 2 unlike
the Y scanlines, add back the correct scanlines calculation
for UBWC YUV formats.

Fixes: 2f3ff6a ("drm/msm/dpu: use standard functions in _dpu_format_populate_plane_sizes_ubwc()")
Fixes: ada4a19 ("drm/msm/dpu: rewrite _dpu_format_populate_plane_sizes_ubwc()")
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Patchwork: https://patchwork.freedesktop.org/patch/718309/
Link: https://lore.kernel.org/r/20260414-topic-sm8x50-msm-dpu1-formats-qc10c-v1-1-0b62325b9030@linaro.org
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
[ Upstream commit d03279f ]

The Kaanapali DPU catalog defines kaanapali_cwb[] with the correct
CWB base addresses for this platform (0x169200, 0x169600, 0x16a200,
0x16a600), but the dpu_kaanapali_cfg struct was mistakenly pointing
to sm8650_cwb instead. The SM8650 CWB blocks sit at completely
different offsets (0x66200, 0x66600, 0x7E200, 0x7E600), so using
them on Kaanapali would program CWB registers at wrong addresses,
corrupting unrelated hardware blocks and breaking writeback capture.

Fix this by pointing .cwb to the correct kaanapali_cwb array.

Fixes: 83fe2cd ("drm/msm/dpu: Add support for Kaanapali DPU")
Signed-off-by: Mahadevan P <mahadevan.p@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Patchwork: https://patchwork.freedesktop.org/patch/721444/
Link: https://lore.kernel.org/r/20260428-kaanapali_cwb-v1-1-51fdb2c65498@oss.qualcomm.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
IntegralPilot and others added 4 commits June 2, 2026 21:54
Add mtp device nodes for t8122 (M3) based MacBooks.

Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: Janne Grunau <j@jannau.net>
List trackpad firmware files and activate MTP devices nodes on all
t6030, t6031 and t6034 based MacBooks.

Signed-off-by: Janne Grunau <j@jannau.net>
jannau and others added 21 commits June 3, 2026 23:09
Add CPU core operating points tables for performance and efficiency
cores and cpufreq nodes using "apple,t8112-cluster-cpufreq" as base
compatible.

Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
Certain Broadcom bluetooth chips (bcm4377/bcm4378/bcm438) need ACL
streams carrying audio to be set as "high priority" using a vendor
specific command to prevent 10-ish second-long dropouts whenever
something does a device scan. This patch sends the command when the
socket priority is set to TC_PRIO_INTERACTIVE, as BlueZ does for audio.

Signed-off-by: Sasha Finkelstein <fnkl.kernel@gmail.com>
Add CPU core operating points tables for performance and efficiency
cores and cpufreq nodes using "apple,t8112-cluster-cpufreq" as base
compatible.

Signed-off-by: Janne Grunau <j@jannau.net>
Values determined by running coremark [1] via following script:
```
#!/bin/sh
set -e
CPUS="$@"

for CPU in ${CPUS}; do
    echo performance > /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_governor
    CUR_FREQ=$(cat /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_cur_freq)
    echo -n "coremark on CPU core ${CPU} at ${CUR_FREQ%000} MHz: "
    taskset -c ${CPU} make run1.log > /dev/null
    grep 'Iterations/Sec' run1.log
    echo schedutil > /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_governor
done
```

Link: https://github.com/eembc/coremark [1]
Signed-off-by: Janne Grunau <j@jannau.net>
Values determined by running coremark [1] via following script:
```
#!/bin/sh
set -e
CPUS="$@"

for CPU in ${CPUS}; do
    echo performance > /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_governor
    CUR_FREQ=$(cat /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_cur_freq)
    echo -n "coremark on CPU core ${CPU} at ${CUR_FREQ%000} MHz: "
    taskset -c ${CPU} make run1.log > /dev/null
    grep 'Iterations/Sec' run1.log
    echo schedutil > /sys/devices/system/cpu/cpu${CPU}/cpufreq/scaling_governor
done
```

Link: https://github.com/eembc/coremark [1]
Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
Values determined using coremark.

Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Janne Grunau <j@jannau.net>
This was actually unnecessary, and having dcp_on_set_parameter as
a dcp_callback_t will introduce some complicated duplication when
enabling VRR. Remove this callback and just set the display handle
on poweron instead.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
DCP supports VRR/Adaptive Sync, with its enormous firmware blob
handling the low-level details for us. Display refresh rate is
determined by the swap timing values provided to DCP on each
swap request.

VRR is activated by setting IOMFBadaptive_sync_parameter::minRR
and then requesting a modeset.

Wire up all of the required KMS properties to expose VRR to
userspace, and tell DCP to enable it when supported. This
enables VRR *unconditionally* for supported sinks, which will
be fixed in a future commit.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
DCP requires a "modeset" to trigger the upload of the SDP
to the display. On some monitors, this is instant. On others,
this seems to take as long as a real modeset. Given that in
either case we still blank the display, let's just force a
full modeset when VRR is toggled on or off.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Setting these timestamps to a dummy value worked fine for enabling
a fixed 120 Hz mode on the MacBook Pros, however doing so causes
Adaptive Sync displays to simply switch between full and minimum
refresh rates. Setting these timestamps based on the swap pacing
seems to fix this, and makes the display's refresh rate match
the incoming swap rate.

Note that the names and values are best-guess only. These seem
to work fine for driving VRR displays, but may still be incorrect.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Since these machines do not have proper EDID/DisplayID data, we need
to help the driver along a little bit. We know that "ProMotion" displays
can do 24-120 Hz VRR, so let's populate the mode with those values.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
macOS is inconsistent with how it uses DCP timestamps. Some swaps
don't use them at all. We know they are required for VRR display
modes to work properly, so let's just turn them on when we are
connected to a VRR display. This includes the 120 Hz mode on
the 14" and 16" MacBook Pros.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Given that DCP requires a modeset to activate VRR, and given that
this is explicitly banned by KMS API contract and VESA DisplayPort
specification, hide this experimental support behind a module param.

Interestingly, the HDMI spec does not require a modeset-free VRR
transition. For this reason, it is expected that the KMS API contract
may change in the future, as both Intel and AMD hardware require
a modeset to enable VRR in some circumstances. Either VRR will be
expected to be enabled whenever it is supported, *or* modesetting
to toggle it on or off will be allowed. When that happens, this
commit *must* be reverted.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
@jannau jannau force-pushed the asahi-wip branch 2 times, most recently from 0f5efb1 to 030248d Compare June 19, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.