Hi,

this is the second attempt of a diff that prepares acpivout(4) to work
with the X395.  The previous one broke due to recursive ACPI locking.

So apparently we cannot change the brightness using the acpivout(4) ACPI
methods.  Instead we need to call amdgpu(4) to change the brightness.
But the brightness key events are still reported by acpivout(4).  That
means that we need to seperate the keystroke events from the actual
brightness change.

This diff changes the code to always use ws_[gs]et_param instead of just
calling the ACPI methods.  This means that the function pointers can
point to acpivout(4) or amdgpu(4), it shouldn't matter.

Unfortunately there's an issue with acpivout(4) calling itself.  The
keystroke event handler runs with the ACPI lock, so if we call our own
function, we try to grab the ACPI lock again and panic.  So, in this
diff I check whether we already have it and skip taking it again.

I'm sure this looks ugly, and I'm wondering if it's too ugly.  If it is
indeed too ugly, I hope you'll also provide another idea how we can work
around it.

More testing would be appreciated.

Thanks,
Patrick

diff --git a/sys/dev/acpi/acpivout.c b/sys/dev/acpi/acpivout.c
index 58e8e3d431c..f3de0c582fb 100644
--- a/sys/dev/acpi/acpivout.c
+++ b/sys/dev/acpi/acpivout.c
@@ -66,6 +66,7 @@ void  acpivout_brightness_cycle(struct acpivout_softc *);
 void   acpivout_brightness_step(struct acpivout_softc *, int);
 void   acpivout_brightness_zero(struct acpivout_softc *);
 int    acpivout_get_brightness(struct acpivout_softc *);
+int    acpivout_select_brightness(struct acpivout_softc *, int);
 int    acpivout_find_brightness(struct acpivout_softc *, int);
 void   acpivout_set_brightness(struct acpivout_softc *, int);
 void   acpivout_get_bcl(struct acpivout_softc *);
@@ -124,6 +125,9 @@ acpivout_notify(struct aml_node *node, int notify, void 
*arg)
 {
        struct acpivout_softc *sc = arg;
 
+       if (ws_get_param == NULL || ws_set_param == NULL)
+               return (0);
+
        switch (notify) {
        case NOTIFY_BRIGHTNESS_CYCLE:
                acpivout_brightness_cycle(sc);
@@ -151,12 +155,13 @@ acpivout_notify(struct aml_node *node, int notify, void 
*arg)
 void
 acpivout_brightness_cycle(struct acpivout_softc *sc)
 {
-       int     cur_level;
+       struct wsdisplay_param dp;
 
-       if (sc->sc_bcl_len == 0)
+       dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+       if (ws_get_param(&dp))
                return;
-       cur_level = acpivout_get_brightness(sc);
-       if (cur_level == sc->sc_bcl[sc->sc_bcl_len - 1])
+
+       if (dp.curval == dp.max)
                acpivout_brightness_zero(sc);
        else
                acpivout_brightness_step(sc, 1);
@@ -165,33 +170,45 @@ acpivout_brightness_cycle(struct acpivout_softc *sc)
 void
 acpivout_brightness_step(struct acpivout_softc *sc, int dir)
 {
-       int level, nindex;
+       struct wsdisplay_param dp;
+       int delta, new;
 
-       if (sc->sc_bcl_len == 0)
-               return;
-       level = acpivout_get_brightness(sc);
-       if (level == -1)
+       dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+       if (ws_get_param(&dp))
                return;
 
-       nindex = acpivout_find_brightness(sc, level + (dir * BRIGHTNESS_STEP));
-       if (sc->sc_bcl[nindex] == level) {
-               if (dir == 1 && (nindex + 1 < sc->sc_bcl_len))
-                       nindex++;
-               else if (dir == -1 && (nindex - 1 >= 0))
-                       nindex--;
+       new = dp.curval;
+       delta = ((dp.max - dp.min) * BRIGHTNESS_STEP) / 100;
+       if (dir > 0) {
+               if (delta > dp.max - dp.curval)
+                       new = dp.max;
+               else
+                       new += delta;
+       } else if (dir < 0) {
+               if (delta > dp.curval - dp.min)
+                       new = dp.min;
+               else
+                       new -= delta;
        }
-       if (sc->sc_bcl[nindex] == level)
+
+       if (dp.curval == new)
                return;
 
-       acpivout_set_brightness(sc, sc->sc_bcl[nindex]);
+       dp.curval = new;
+       ws_set_param(&dp);
 }
 
 void
 acpivout_brightness_zero(struct acpivout_softc *sc)
 {
-       if (sc->sc_bcl_len == 0)
+       struct wsdisplay_param dp;
+
+       dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+       if (ws_get_param(&dp))
                return;
-       acpivout_set_brightness(sc, sc->sc_bcl[0]);
+
+       dp.curval = dp.min;
+       ws_set_param(&dp);
 }
 
 int
@@ -211,6 +228,26 @@ acpivout_get_brightness(struct acpivout_softc *sc)
        return (level);
 }
 
+int
+acpivout_select_brightness(struct acpivout_softc *sc, int nlevel)
+{
+       int nindex, level;
+
+       level = acpivout_get_brightness(sc);
+       if (level == -1)
+               return level;
+
+       nindex = acpivout_find_brightness(sc, nlevel);
+       if (sc->sc_bcl[nindex] == level) {
+               if (nlevel > level && (nindex + 1 < sc->sc_bcl_len))
+                       nindex++;
+               else if (nlevel < level && (nindex - 1 >= 0))
+                       nindex--;
+       }
+
+       return nindex;
+}
+
 int
 acpivout_find_brightness(struct acpivout_softc *sc, int level)
 {
@@ -290,7 +327,7 @@ int
 acpivout_get_param(struct wsdisplay_param *dp)
 {
        struct acpivout_softc   *sc = NULL;
-       int i;
+       int i, needlock;
 
        switch (dp->param) {
        case WSDISPLAYIO_PARAM_BRIGHTNESS:
@@ -304,10 +341,13 @@ acpivout_get_param(struct wsdisplay_param *dp)
                }
                if (sc != NULL && sc->sc_bcl_len != 0) {
                        dp->min = 0;
-                       dp->max =  sc->sc_bcl[sc->sc_bcl_len - 1];
-                       rw_enter_write(&sc->sc_acpi->sc_lck);
+                       dp->max = sc->sc_bcl[sc->sc_bcl_len - 1];
+                       needlock = rw_status(&sc->sc_acpi->sc_lck) != RW_WRITE;
+                       if (needlock)
+                               rw_enter_write(&sc->sc_acpi->sc_lck);
                        dp->curval = acpivout_get_brightness(sc);
-                       rw_exit_write(&sc->sc_acpi->sc_lck);
+                       if (needlock)
+                               rw_exit_write(&sc->sc_acpi->sc_lck);
                        if (dp->curval != -1)
                                return 0;
                }
@@ -321,7 +361,7 @@ int
 acpivout_set_param(struct wsdisplay_param *dp)
 {
        struct acpivout_softc   *sc = NULL;
-       int i, nindex;
+       int i, nindex, needlock;
 
        switch (dp->param) {
        case WSDISPLAYIO_PARAM_BRIGHTNESS:
@@ -334,10 +374,14 @@ acpivout_set_param(struct wsdisplay_param *dp)
                                break;
                }
                if (sc != NULL && sc->sc_bcl_len != 0) {
-                       rw_enter_write(&sc->sc_acpi->sc_lck);
-                       nindex = acpivout_find_brightness(sc, dp->curval);
-                       acpivout_set_brightness(sc, sc->sc_bcl[nindex]);
-                       rw_exit_write(&sc->sc_acpi->sc_lck);
+                       needlock = rw_status(&sc->sc_acpi->sc_lck) != RW_WRITE;
+                       if (needlock)
+                               rw_enter_write(&sc->sc_acpi->sc_lck);
+                       nindex = acpivout_select_brightness(sc, dp->curval);
+                       if (nindex != -1)
+                               acpivout_set_brightness(sc, sc->sc_bcl[nindex]);
+                       if (needlock)
+                               rw_exit_write(&sc->sc_acpi->sc_lck);
                        return 0;
                }
                return -1;

Reply via email to