diff --git a/.circleci/config.yml b/.circleci/config.yml
index 189a0a5..fe05545 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -514,11 +514,11 @@ workflows:
     jobs:
       - build_docs:
           scheduled: "true"
-          name: build_docs_main
+          name: build_docs_stable
       - deploy:
-          name: deploy_main
+          name: deploy_stable
           requires:
-            - build_docs_main
+            - build_docs_stable
     triggers:
       - schedule:
           # "At 00:00" (once a day) should be enough "0 0 * * *",
diff --git a/.github/workflows/codespell_and_flake.yml b/.github/workflows/codespell_and_flake.yml
index 2a2469f..89e02c5 100644
--- a/.github/workflows/codespell_and_flake.yml
+++ b/.github/workflows/codespell_and_flake.yml
@@ -41,4 +41,5 @@ jobs:
           quiet_level: '3'
           builtin: 'clear,rare,informal,names'
           ignore_words_file: 'ignore_words.txt'
+          uri_ignore_words_list: 'bu'
         name: 'Run codespell'
diff --git a/Makefile b/Makefile
index 8f124c4..6ed3939 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 PYTHON ?= python
 PYTESTS ?= py.test
 CTAGS ?= ctags
-CODESPELL_SKIPS ?= "doc/auto_*,*.fif,*.eve,*.gz,*.tgz,*.zip,*.mat,*.stc,*.label,*.w,*.bz2,*.annot,*.sulc,*.log,*.local-copy,*.orig_avg,*.inflated_avg,*.gii,*.pyc,*.doctree,*.pickle,*.inv,*.png,*.edf,*.touch,*.thickness,*.nofix,*.volume,*.defect_borders,*.mgh,lh.*,rh.*,COR-*,FreeSurferColorLUT.txt,*.examples,.xdebug_mris_calc,bad.segments,BadChannels,*.hist,empty_file,*.orig,*.js,*.map,*.ipynb,searchindex.dat,install_mne_c.rst,plot_*.rst,*.rst.txt,c_EULA.rst*,*.html,gdf_encodes.txt,*.svg,references.bib,*.css"
+CODESPELL_SKIPS ?= "doc/_build,doc/auto_*,*.fif,*.eve,*.gz,*.tgz,*.zip,*.mat,*.stc,*.label,*.w,*.bz2,*.annot,*.sulc,*.log,*.local-copy,*.orig_avg,*.inflated_avg,*.gii,*.pyc,*.doctree,*.pickle,*.inv,*.png,*.edf,*.touch,*.thickness,*.nofix,*.volume,*.defect_borders,*.mgh,lh.*,rh.*,COR-*,FreeSurferColorLUT.txt,*.examples,.xdebug_mris_calc,bad.segments,BadChannels,*.hist,empty_file,*.orig,*.js,*.map,*.ipynb,searchindex.dat,install_mne_c.rst,plot_*.rst,*.rst.txt,c_EULA.rst*,*.html,gdf_encodes.txt,*.svg,references.bib,*.css"
 CODESPELL_DIRS ?= mne/ doc/ tutorials/ examples/
 all: clean inplace test test-doc
 
@@ -103,10 +103,10 @@ flake:
 	@echo "flake8 passed"
 
 codespell:  # running manually
-	@codespell --builtin clear,rare,informal,names,usage -w -i 3 -q 3 -S $(CODESPELL_SKIPS) --ignore-words=ignore_words.txt $(CODESPELL_DIRS)
+	@codespell --builtin clear,rare,informal,names,usage -w -i 3 -q 3 -S $(CODESPELL_SKIPS) --ignore-words=ignore_words.txt --uri-ignore-words-list=bu $(CODESPELL_DIRS)
 
 codespell-error:  # running on travis
-	@codespell --builtin clear,rare,informal,names,usage -i 0 -q 7 -S $(CODESPELL_SKIPS) --ignore-words=ignore_words.txt $(CODESPELL_DIRS)
+	@codespell --builtin clear,rare,informal,names,usage -i 0 -q 7 -S $(CODESPELL_SKIPS) --ignore-words=ignore_words.txt --uri-ignore-words-list=bu $(CODESPELL_DIRS)
 
 pydocstyle:
 	@echo "Running pydocstyle"
diff --git a/debian/changelog b/debian/changelog
index f9fa7a6..85ff5d6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,13 @@
-python-mne (1.1.0+dfsg-2) UNRELEASED; urgency=medium
+python-mne (1.1.1+dfsg-1) UNRELEASED; urgency=medium
 
+  [ Andreas Tille ]
   * Team upload.
   * Create proper source tarball name
 
- -- Andreas Tille <tille@debian.org>  Thu, 04 Aug 2022 14:55:46 +0200
+  [ Debian Janitor ]
+  * New upstream release.
+
+ -- Andreas Tille <tille@debian.org>  Sat, 03 Sep 2022 16:24:03 -0000
 
 python-mne (1.1.0+dfsg-1) unstable; urgency=medium
 
diff --git a/debian/patches/up_check_numpy b/debian/patches/up_check_numpy
index 097ce70..8779fe8 100644
--- a/debian/patches/up_check_numpy
+++ b/debian/patches/up_check_numpy
@@ -4,8 +4,10 @@ Description: Check for numpy version
 FIXME: Is this patch needed any more in version 0.17?
 Last-Update: 2019-09-01
 
---- a/mne/decoding/tests/test_receptive_field.py
-+++ b/mne/decoding/tests/test_receptive_field.py
+Index: python-mne/mne/decoding/tests/test_receptive_field.py
+===================================================================
+--- python-mne.orig/mne/decoding/tests/test_receptive_field.py
++++ python-mne/mne/decoding/tests/test_receptive_field.py
 @@ -10,7 +10,7 @@ from numpy import einsum
  from numpy.fft import rfft, irfft
  from numpy.testing import assert_array_equal, assert_allclose, assert_equal
diff --git a/doc/_includes/channel_types.rst b/doc/_includes/channel_types.rst
index be2abe2..6c0adc1 100644
--- a/doc/_includes/channel_types.rst
+++ b/doc/_includes/channel_types.rst
@@ -53,7 +53,7 @@ bio            Miscellaneous biological channels (e.g.,  Arbitrary units
 
 stim           stimulus (a.k.a. trigger) channels        Arbitrary units
 
-resp           response-trigger channel                  Arbitrary units
+resp           respiration monitoring channel            Volts
 
 chpi           continuous head position indicator        Teslas
                (HPI) coil channels
diff --git a/doc/changes/0.23.inc b/doc/changes/0.23.inc
index c1c6458..fdcaeb6 100644
--- a/doc/changes/0.23.inc
+++ b/doc/changes/0.23.inc
@@ -292,7 +292,7 @@ Bugs
 
 - When creating `~mne.Epochs`, we now ensure that ``reject_tmin`` and ``reject_tmax`` cannot fall outside of the epochs' time interval anymore (:gh:`8821` by `Richard Höchenberger`_)
 
-- `~mne.io.read_raw_bti` erroneously treated response channels as respiratory channels (:gh:`8855` by `Richard Höchenberger`_)
+- `~mne.io.read_raw_bti` erroneously treated response channels as respiratory channels (:gh:`8856` by `Richard Höchenberger`_)
 
 - The RMS trace shown in the time viewer of `~mne.SourceEstimate` plots is now correctly labeled as ``RMS`` (was ``GFP`` before) (:gh:`8965` by `Richard Höchenberger`_)
 
diff --git a/doc/changes/1.1.inc b/doc/changes/1.1.inc
index fcb2be1..c60b7b0 100644
--- a/doc/changes/1.1.inc
+++ b/doc/changes/1.1.inc
@@ -16,6 +16,15 @@
 
    Also add a corresponding entry for yourself in doc/changes/names.inc
 
+.. _changes_1_1_1:
+
+Version 1.1.1 (2022-08-24)
+--------------------------
+
+Bug
+---
+- Fix bug in :func:`mne.io.read_raw_eeglab` where unlabeled fiducials caused reading errors (:gh:`11074` by :newcontrib:`Sebastiaan Mathot`)
+
 .. _changes_1_1_0:
 
 Version 1.1.0 (2022-08-03)
diff --git a/doc/changes/names.inc b/doc/changes/names.inc
index c5216a1..f048531 100644
--- a/doc/changes/names.inc
+++ b/doc/changes/names.inc
@@ -406,6 +406,8 @@
 
 .. _Scott Huberty:  https://orcid.org/0000-0003-2637-031X
 
+.. _Sebastiaan Mathot: http://www.cogsci.nl/smathot
+
 .. _Sebastian Castano: https://github.com/jscastanoc
 
 .. _Sebastian Major: https://github.com/major-s
diff --git a/doc/install/installers.rst b/doc/install/installers.rst
index 2ace4f1..eee833d 100644
--- a/doc/install/installers.rst
+++ b/doc/install/installers.rst
@@ -16,7 +16,7 @@ Got any questions? Let us know on the `MNE Forum`_!
     .. tab-item:: Linux
         :class-content: text-center
 
-        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.0.3/MNE-Python-1.0.3_0-Linux.sh
+        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.1.0/MNE-Python-1.1.0_0-Linux.sh
             :ref-type: ref
             :color: primary
             :shadow:
@@ -30,28 +30,43 @@ Got any questions? Let us know on the `MNE Forum`_!
 
         .. code-block:: console
 
-            $ sh ./MNE-Python-1.0.3_0-Linux.sh
+            $ sh ./MNE-Python-1.1.0_0-Linux.sh
 
 
-    .. tab-item:: macOS
+    .. tab-item:: macOS (Intel)
         :class-content: text-center
 
-        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.0.3/MNE-Python-1.0.3_0-macOS_Intel.pkg
+        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.1.0/MNE-Python-1.1.0_0-macOS_Intel.pkg
             :ref-type: ref
             :color: primary
             :shadow:
             :class: font-weight-bold mt-3
 
-            |cloud-download-alt| |ensp| Download for macOS
+            |cloud-download-alt| |ensp| Download for macOS (Intel)
 
         **Supported platforms:**
-        macOS 10.15 (Catalina) and newer (Intel and Apple Silicon)
+        macOS 10.15 (Catalina) and newer
 
 
+    .. tab-item:: macOS (Apple Silicon)
+        :class-content: text-center
+
+        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.1.0/MNE-Python-1.1.0_0-macOS_M1.pkg
+            :ref-type: ref
+            :color: primary
+            :shadow:
+            :class: font-weight-bold mt-3
+
+            |cloud-download-alt| |ensp| Download for macOS (Apple Silicon)
+
+
+        **Supported platforms:**
+        macOS 10.15 (Catalina) and newer
+
     .. tab-item:: Windows
         :class-content: text-center
 
-        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.0.3/MNE-Python-1.0.3_0-Windows.exe
+        .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.1.0/MNE-Python-1.1.0_0-Windows.exe
             :ref-type: ref
             :color: primary
             :shadow:
diff --git a/doc/sphinxext/gen_commands.py b/doc/sphinxext/gen_commands.py
index 38c6b88..e0169a4 100644
--- a/doc/sphinxext/gen_commands.py
+++ b/doc/sphinxext/gen_commands.py
@@ -49,7 +49,7 @@ command_rst = """
 
 
 def generate_commands_rst(app=None):
-    from sphinx_gallery import sphinx_compatibility
+    from sphinx.util import status_iterator
     out_dir = op.abspath(op.join(op.dirname(__file__), '..', 'generated'))
     if not op.isdir(out_dir):
         os.mkdir(out_dir)
@@ -60,7 +60,7 @@ def generate_commands_rst(app=None):
     fnames = sorted([
         op.basename(fname)
         for fname in glob.glob(op.join(command_path, 'mne_*.py'))])
-    iterator = sphinx_compatibility.status_iterator(
+    iterator = status_iterator(
         fnames, 'generating MNE command help ... ', length=len(fnames))
     with open(out_fname, 'w', encoding='utf8') as f:
         f.write(header)
diff --git a/ignore_words.txt b/ignore_words.txt
index 0a6b425..820e023 100644
--- a/ignore_words.txt
+++ b/ignore_words.txt
@@ -37,3 +37,5 @@ nin
 trough
 recuse
 ro
+nam
+bu
diff --git a/mne/_version.py b/mne/_version.py
index 55e6fa2..145e142 100644
--- a/mne/_version.py
+++ b/mne/_version.py
@@ -3,4 +3,4 @@
 #
 # License: BSD-3-Clause
 
-__version__ = '1.1.0'
+__version__ = '1.1.1'
diff --git a/mne/coreg.py b/mne/coreg.py
index cde589e..cbdec77 100644
--- a/mne/coreg.py
+++ b/mne/coreg.py
@@ -1764,7 +1764,7 @@ class Coregistration(object):
         self._log_dig_mri_distance('Start')
         n_scale_params = self._n_scale_params
         if n_scale_params == 3:
-            # enfore 1 even for 3-axis here (3 points is not enough)
+            # enforce 1 even for 3-axis here (3 points is not enough)
             logger.info("Enforcing 1 scaling parameter for fit "
                         "with fiducials.")
             n_scale_params = 1
diff --git a/mne/decoding/ssd.py b/mne/decoding/ssd.py
index f6ac42b..4a14d6c 100644
--- a/mne/decoding/ssd.py
+++ b/mne/decoding/ssd.py
@@ -184,7 +184,7 @@ class SSD(BaseEstimator, TransformerMixin):
         self.filters_ = eigvects_[:, ix]
         self.patterns_ = np.linalg.pinv(self.filters_)
         # We assume that ordering by spectral ratio is more important
-        # than the initial ordering. This ording should be also learned when
+        # than the initial ordering. This ordering should be also learned when
         # fitting.
         X_ssd = self.filters_.T @ X[..., self.picks_, :]
         sorter_spec = Ellipsis
diff --git a/mne/evoked.py b/mne/evoked.py
index 6933fc8..4925626 100644
--- a/mne/evoked.py
+++ b/mne/evoked.py
@@ -269,7 +269,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin, SetChannelsMixin,
                                    sfreq=self.info['sfreq'])
         if self.baseline is not None and baseline is None:
             raise ValueError('The data has already been baseline-corrected. '
-                             'Cannot remove existing basline correction.')
+                             'Cannot remove existing baseline correction.')
         elif baseline is None:
             # Do not rescale
             logger.info(_log_rescale(None))
diff --git a/mne/io/brainvision/brainvision.py b/mne/io/brainvision/brainvision.py
index c177c7b..f2e55b4 100644
--- a/mne/io/brainvision/brainvision.py
+++ b/mne/io/brainvision/brainvision.py
@@ -931,7 +931,7 @@ def _parse_impedance(settings, recording_date=None):
     Parameters
     ----------
     settings : list
-        The header settings lines fom the VHDR/AHDR file.
+        The header settings lines from the VHDR/AHDR file.
     recording_date : datetime.datetime | None
         The date of the recording as extracted from the VMRK/AMRK file.
 
@@ -993,7 +993,7 @@ def _parse_impedance_ranges(settings):
     Parameters
     ----------
     settings : list
-        The header settings lines fom the VHDR/AHDR file.
+        The header settings lines from the VHDR/AHDR file.
 
     Returns
     -------
diff --git a/mne/io/curry/curry.py b/mne/io/curry/curry.py
index f3cb506..e298be9 100644
--- a/mne/io/curry/curry.py
+++ b/mne/io/curry/curry.py
@@ -393,7 +393,7 @@ def _first_hpi(fname):
             break
         else:
             raise RuntimeError('Could not find valid HPI in %s' % (fname,))
-    # t is the first enttry
+    # t is the first entry
     assert hpi.ndim == 1
     hpi = hpi[1:]
     hpi.shape = (-1, 5)
diff --git a/mne/io/eeglab/_eeglab.py b/mne/io/eeglab/_eeglab.py
new file mode 100644
index 0000000..71b0ce5
--- /dev/null
+++ b/mne/io/eeglab/_eeglab.py
@@ -0,0 +1,82 @@
+import numpy as np
+
+from ...utils import _import_pymatreader_funcs
+
+
+def _todict_from_np_struct(data):  # taken from pymatreader.utils
+    data_dict = {}
+
+    for cur_field_name in data.dtype.names:
+        try:
+            n_items = len(data[cur_field_name])
+            cur_list = []
+
+            for idx in np.arange(n_items):
+                cur_value = data[cur_field_name].item(idx)
+                cur_value = _check_for_scipy_mat_struct(cur_value)
+                cur_list.append(cur_value)
+
+            data_dict[cur_field_name] = cur_list
+        except TypeError:
+            cur_value = data[cur_field_name].item(0)
+            cur_value = _check_for_scipy_mat_struct(cur_value)
+            data_dict[cur_field_name] = cur_value
+
+    return data_dict
+
+
+def _handle_scipy_ndarray(data):  # taken from pymatreader.utils
+    try:
+        from scipy.io.matlab import MatlabFunction
+    except ImportError:  # scipy < 1.8
+        from scipy.io.matlab.mio5 import MatlabFunction
+
+    if data.dtype == np.dtype('object') and not \
+            isinstance(data, MatlabFunction):
+        as_list = []
+        for element in data:
+            as_list.append(_check_for_scipy_mat_struct(element))
+        data = as_list
+    elif isinstance(data.dtype.names, tuple):
+        data = _todict_from_np_struct(data)
+        data = _check_for_scipy_mat_struct(data)
+
+    if isinstance(data, np.ndarray):
+        data = np.array(data)
+
+    return data
+
+
+def _check_for_scipy_mat_struct(data):  # taken from pymatreader.utils
+    """Convert all scipy.io.matlab.mio5_params.mat_struct elements."""
+    try:
+        from scipy.io.matlab import MatlabOpaque
+    except ImportError:  # scipy < 1.8
+        from scipy.io.matlab.mio5_params import MatlabOpaque
+
+    if isinstance(data, dict):
+        for key in data:
+            data[key] = _check_for_scipy_mat_struct(data[key])
+
+    if isinstance(data, MatlabOpaque):
+        try:
+            if data[0][2] == b'string':
+                return None
+        except IndexError:
+            pass
+
+    if isinstance(data, np.ndarray):
+        data = _handle_scipy_ndarray(data)
+
+    return data
+
+
+def _readmat(fname, uint16_codec=None):
+    try:
+        read_mat = _import_pymatreader_funcs('EEGLAB I/O')
+    except RuntimeError:  # pymatreader not installed
+        from scipy.io import loadmat
+        eeg = loadmat(fname, squeeze_me=True, mat_dtype=False)
+        return _check_for_scipy_mat_struct(eeg)
+    else:
+        return read_mat(fname, uint16_codec=uint16_codec)
diff --git a/mne/io/eeglab/eeglab.py b/mne/io/eeglab/eeglab.py
index d2697eb..57558b8 100644
--- a/mne/io/eeglab/eeglab.py
+++ b/mne/io/eeglab/eeglab.py
@@ -8,13 +8,13 @@ import os.path as op
 
 import numpy as np
 
+from ._eeglab import _readmat
 from ..pick import _PICK_TYPES_KEYS
 from ..utils import _read_segments_file, _find_channels
 from ..constants import FIFF
 from ..meas_info import create_info
 from ..base import BaseRaw
-from ...utils import (logger, verbose, warn, fill_doc, Bunch, _check_fname,
-                      _import_pymatreader_funcs)
+from ...utils import logger, verbose, warn, fill_doc, Bunch, _check_fname
 from ...channels import make_dig_montage
 from ...epochs import BaseEpochs
 from ...event import read_events
@@ -57,8 +57,7 @@ def _check_eeglab_fname(fname, dataname):
 
 def _check_load_mat(fname, uint16_codec):
     """Check if the mat struct contains 'EEG'."""
-    read_mat = _import_pymatreader_funcs('EEGLAB I/O')
-    eeg = read_mat(fname, uint16_codec=uint16_codec)
+    eeg = _readmat(fname, uint16_codec=uint16_codec)
     if 'ALLEEG' in eeg:
         raise NotImplementedError(
             'Loading an ALLEEG array is not supported. Please contact'
@@ -144,18 +143,16 @@ def _get_montage_information(eeg, get_pos):
                         for key in sorted(unknown_types)]))
 
     lpa, rpa, nasion = None, None, None
-    if (hasattr(eeg, "chaninfo") and
-            "nodatchans" in eeg.chaninfo and
-            len(eeg.chaninfo['nodatchans'])):
+    if hasattr(eeg, "chaninfo") and len(eeg.chaninfo.get('nodatchans', [])):
         for item in list(zip(*eeg.chaninfo['nodatchans'].values())):
             d = dict(zip(eeg.chaninfo['nodatchans'].keys(), item))
-            if d["type"] != 'FID':
+            if d.get("type", None) != 'FID':
                 continue
-            elif d['description'] == 'Nasion':
+            elif d.get('description', None) == 'Nasion':
                 nasion = np.array([d["X"], d["Y"], d["Z"]])
-            elif d['description'] == 'Right periauricular point':
+            elif d.get('description', None) == 'Right periauricular point':
                 rpa = np.array([d["X"], d["Y"], d["Z"]])
-            elif d['description'] == 'Left periauricular point':
+            elif d.get('description', None) == 'Left periauricular point':
                 lpa = np.array([d["X"], d["Y"], d["Z"]])
 
     if pos_ch_names:
diff --git a/mne/io/eeglab/tests/test_eeglab.py b/mne/io/eeglab/tests/test_eeglab.py
index 5b5ac26..598d908 100644
--- a/mne/io/eeglab/tests/test_eeglab.py
+++ b/mne/io/eeglab/tests/test_eeglab.py
@@ -14,13 +14,15 @@ from numpy.testing import (assert_array_equal, assert_array_almost_equal,
 import pytest
 from scipy import io
 
+import mne
 from mne import write_events, read_epochs_eeglab
 from mne.channels import read_custom_montage
 from mne.io import read_raw_eeglab
 from mne.io.eeglab.eeglab import _get_montage_information, _dol_to_lod
+from mne.io.eeglab._eeglab import _readmat
 from mne.io.tests.test_raw import _test_raw_reader
 from mne.datasets import testing
-from mne.utils import Bunch
+from mne.utils import Bunch, _check_pymatreader_installed
 from mne.annotations import events_from_annotations, read_annotations
 
 base_dir = op.join(testing.data_path(download=False), 'EEGLAB')
@@ -45,13 +47,18 @@ epochs_h5_fnames = [epochs_fname_h5, epochs_fname_onefile_h5]
 montage_path = op.join(base_dir, 'test_chans.locs')
 
 
-pymatreader = pytest.importorskip('pymatreader')  # module-level
-
-
 @testing.requires_testing_data
 @pytest.mark.parametrize('fname', [
     raw_fname_mat,
-    raw_fname_h5,
+    pytest.param(
+        raw_fname_h5,
+        marks=[
+            pytest.mark.skipif(
+                not _check_pymatreader_installed(strict=False),
+                reason='pymatreader not installed'
+            )
+        ]
+    ),
     raw_fname_chanloc,
 ], ids=op.basename)
 def test_io_set_raw(fname):
@@ -242,7 +249,16 @@ def test_io_set_raw_more(tmp_path):
 @testing.requires_testing_data
 @pytest.mark.parametrize('fnames', [
     epochs_mat_fnames,
-    pytest.param(epochs_h5_fnames, marks=[pytest.mark.slowtest]),
+    pytest.param(
+        epochs_h5_fnames,
+        marks=[
+            pytest.mark.slowtest,
+            pytest.mark.skipif(
+                not _check_pymatreader_installed(strict=False),
+                reason='pymatreader not installed'
+            )
+        ]
+    )
 ])
 def test_io_set_epochs(fnames):
     """Test importing EEGLAB .set epochs files."""
@@ -433,7 +449,7 @@ def test_read_single_epoch():
 @testing.requires_testing_data
 def test_get_montage_info_with_ch_type():
     """Test that the channel types are properly returned."""
-    mat = pymatreader.read_mat(raw_fname_onefile_mat, uint16_codec=None)
+    mat = _readmat(raw_fname_onefile_mat)
     n = len(mat['EEG']['chanlocs']['labels'])
     mat['EEG']['chanlocs']['type'] = ['eeg'] * (n - 2) + ['eog'] + ['stim']
     mat['EEG']['chanlocs'] = _dol_to_lod(mat['EEG']['chanlocs'])
@@ -444,7 +460,7 @@ def test_get_montage_info_with_ch_type():
     assert montage is None
 
     # test unknown type warning
-    mat = pymatreader.read_mat(raw_fname_onefile_mat, uint16_codec=None)
+    mat = _readmat(raw_fname_onefile_mat)
     n = len(mat['EEG']['chanlocs']['labels'])
     mat['EEG']['chanlocs']['type'] = ['eeg'] * (n - 2) + ['eog'] + ['unknown']
     mat['EEG']['chanlocs'] = _dol_to_lod(mat['EEG']['chanlocs'])
@@ -455,14 +471,30 @@ def test_get_montage_info_with_ch_type():
 
 
 @testing.requires_testing_data
-def test_fidsposition_information():
+@pytest.mark.parametrize('has_type', (True, False))
+def test_fidsposition_information(monkeypatch, has_type):
     """Test reading file with 3 fiducial locations."""
+    if not has_type:
+        def get_bad_information(eeg, get_pos):
+            del eeg.chaninfo['nodatchans']['type']
+            return _get_montage_information(eeg, get_pos)
+
+        monkeypatch.setattr(mne.io.eeglab.eeglab, '_get_montage_information',
+                            get_bad_information)
     raw = read_raw_eeglab(raw_fname_chanloc_fids)
     montage = raw.get_montage()
     pos = montage.get_positions()
+    n_eeg = 129
+    if not has_type:
+        assert pos['nasion'] is None
+        assert pos['lpa'] is None
+        assert pos['rpa'] is None
+        assert len(raw.info['dig']) == n_eeg
+        return
     assert pos['nasion'] is not None
     assert pos['lpa'] is not None
     assert pos['rpa'] is not None
     assert len(pos['nasion']) == 3
     assert len(pos['lpa']) == 3
     assert len(pos['rpa']) == 3
+    assert len(raw.info['dig']) == n_eeg + 3
diff --git a/mne/io/nihon/nihon.py b/mne/io/nihon/nihon.py
index 00318a1..cd98d14 100644
--- a/mne/io/nihon/nihon.py
+++ b/mne/io/nihon/nihon.py
@@ -392,7 +392,7 @@ class RawNihon(BaseRaw):
         # Get annotations from LOG file
         annots = _read_nihon_annotations(fname)
 
-        # Annotate acqusition skips
+        # Annotate acquisition skips
         controlblock = self._header['controlblocks'][0]
         cur_sample = 0
         if controlblock['n_datablocks'] > 1:
diff --git a/mne/preprocessing/tests/test_eeglab_infomax.py b/mne/preprocessing/tests/test_eeglab_infomax.py
index 5a59f22..c83841b 100644
--- a/mne/preprocessing/tests/test_eeglab_infomax.py
+++ b/mne/preprocessing/tests/test_eeglab_infomax.py
@@ -76,7 +76,7 @@ def test_mne_python_vs_eeglab():
                                    % (method,
                                       dict(eeg='eeg', mag='meg')[ch_type]))
 
-            # For comparasion against eeglab, make sure the following
+            # For comparison against eeglab, make sure the following
             # parameters have the same value in mne_python and eeglab:
             #
             # - starting point
diff --git a/mne/utils/__init__.py b/mne/utils/__init__.py
index c63ea86..ea58d9c 100644
--- a/mne/utils/__init__.py
+++ b/mne/utils/__init__.py
@@ -21,7 +21,7 @@ from .check import (check_fname, check_version, check_random_state,
                     _check_all_same_channel_names, path_like, _ensure_events,
                     _check_eeglabio_installed, _check_pybv_installed,
                     _check_edflib_installed, _to_rgb, _soft_import,
-                    _check_dict_keys,
+                    _check_dict_keys, _check_pymatreader_installed,
                     _import_h5py, _import_h5io_funcs,
                     _import_pymatreader_funcs)
 from .config import (set_config, get_config, get_config_path, set_cache_dir,
diff --git a/mne/utils/check.py b/mne/utils/check.py
index 73fa579..c37fe43 100644
--- a/mne/utils/check.py
+++ b/mne/utils/check.py
@@ -384,6 +384,15 @@ def _check_pybv_installed(strict=True):
     return _soft_import('pybv', 'exporting to BrainVision', strict=strict)
 
 
+def _check_pymatreader_installed(strict=True):
+    """Aux function."""
+    return _soft_import(
+        'pymatreader',
+        'loading v7.3 (HDF5) .MAT files',
+        strict=strict
+    )
+
+
 def _check_pandas_index_arguments(index, valid):
     """Check pandas index arguments."""
     if index is None:
diff --git a/mne/utils/dataframe.py b/mne/utils/dataframe.py
index b47dd9b..16956bb 100644
--- a/mne/utils/dataframe.py
+++ b/mne/utils/dataframe.py
@@ -8,6 +8,7 @@ import numpy as np
 
 from ._logging import logger, verbose
 from ..defaults import _handle_default
+from ..fixes import _get_args
 
 
 @verbose
@@ -47,6 +48,16 @@ def _convert_times(inst, times, time_format):
     return times
 
 
+def _inplace(df, method, **kwargs):
+    """Handle transition: inplace=True (pandas <1.5) → copy=False (>=1.5)."""
+    _meth = getattr(df, method)  # used for set_index() and rename()
+    if 'copy' in _get_args(_meth):
+        return _meth(**kwargs, copy=False)
+    else:
+        _meth(**kwargs, inplace=True)
+        return df
+
+
 @verbose
 def _build_data_frame(inst, data, picks, long_format, mindex, index,
                       default_index, col_names=None, col_kind='channel',
@@ -63,16 +74,16 @@ def _build_data_frame(inst, data, picks, long_format, mindex, index,
         df.insert(i, k, v)
     # build Index
     if long_format:
-        df.set_index(default_index, inplace=True)
+        df = _inplace(df, 'set_index', keys=default_index)
         df.columns.name = col_kind
     elif index is not None:
-        df.set_index(index, inplace=True)
+        df = _inplace(df, 'set_index', keys=index)
         if set(index) == set(default_index):
             df.columns.name = col_kind
     # long format
     if long_format:
         df = df.stack().reset_index()
-        df.rename(columns={0: 'value'}, inplace=True)
+        df = _inplace(df, 'rename', columns={0: 'value'})
         # add column for channel types (as appropriate)
         ch_map = (None if isinstance(inst, _BaseSourceEstimate) else
                   dict(zip(np.array(inst.ch_names)[picks],
@@ -83,7 +94,7 @@ def _build_data_frame(inst, data, picks, long_format, mindex, index,
             df.insert(col_index, 'ch_type', ch_type)
         # restore index
         if index is not None:
-            df.set_index(index, inplace=True)
+            df = _inplace(df, 'set_index', keys=index)
         # convert channel/vertex/ch_type columns to factors
         to_factor = [c for c in df.columns.tolist()
                      if c not in ('time', 'value')]
diff --git a/tutorials/stats-sensor-space/75_cluster_ftest_spatiotemporal.py b/tutorials/stats-sensor-space/75_cluster_ftest_spatiotemporal.py
index 4f29376..345cea3 100644
--- a/tutorials/stats-sensor-space/75_cluster_ftest_spatiotemporal.py
+++ b/tutorials/stats-sensor-space/75_cluster_ftest_spatiotemporal.py
@@ -141,7 +141,7 @@ F_obs, clusters, p_values, _ = cluster_stats
 #           an adjacency for time points was automatically taken into
 #           account. That is, at time point N, the time points N - 1 and
 #           N + 1 were considered as adjacent (this is also called "lattice
-#           adjacency"). This is only possbile because we ran the analysis on
+#           adjacency"). This is only possible because we ran the analysis on
 #           2D data (times × channels) per observation ... for 3D data per
 #           observation (e.g., times × frequencies × channels), we will need
 #           to use :func:`mne.stats.combine_adjacency`, as shown further