Subject: Backlight support for radeonfb
To: None <port-macppc@netbsd.org>
From: Johan =?iso-8859-1?Q?Wall=E9n?= <johan.wallen+lists@tkk.fi>
List: port-macppc
Date: 12/17/2006 17:49:43
Hello,
the following patch adds rudimentary backlight support to radeonfb(4).
Some of the Radeon chips found on PowerBooks have, according to the
Linux radeon_backlight driver, the backlight levels negated (higher
levels give less backlight). You should be able to work around this
using `options RADEONFB_BACKLIGHT_NEGATED', but I have not tested it.
On Radeon Mobility 9700 `wsconsctl -d -w backlight=xxx' seems to
work as expected.
-- Johan
Index: radeonfb.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/radeonfb.c,v
retrieving revision 1.8
diff -u -r1.8 radeonfb.c
--- radeonfb.c 13 Dec 2006 00:19:01 -0000 1.8
+++ radeonfb.c 17 Dec 2006 15:25:00 -0000
@@ -163,6 +163,10 @@
static void radeonfb_putchar(void *, int, int, unsigned, long);
static int radeonfb_allocattr(void *, int, int, int, long *);
+static int radeonfb_get_backlight(struct radeonfb_display *);
+static int radeonfb_set_backlight(struct radeonfb_display *, int);
+static void radeonfb_lvds_callout(void *);
+
static struct videomode *radeonfb_best_refresh(struct videomode *,
struct videomode *);
static void radeonfb_pickres(struct radeonfb_display *, uint16_t *,
@@ -394,6 +398,8 @@
{ RADEON_R420, {{-1, 0xb01cb}}},
};
+#define RADEONFB_BACKLIGHT_MAX 255 /* Maximum backlight level. */
+
CFATTACH_DECL(radeonfb, sizeof (struct radeonfb_softc),
radeonfb_match, radeonfb_attach, NULL, NULL);
@@ -866,6 +872,11 @@
config_found(&sc->sc_dev, &aa, wsemuldisplaydevprint);
radeonfb_blank(dp, 0);
+
+ /* Initialise delayed lvds operations for backlight. */
+ callout_init(&dp->rd_bl_lvds_co);
+ callout_setfunc(&dp->rd_bl_lvds_co,
+ radeonfb_lvds_callout, dp);
}
return;
@@ -888,6 +899,7 @@
struct vcons_data *vd;
struct radeonfb_display *dp;
struct radeonfb_softc *sc;
+ struct wsdisplay_param *param;
vd = (struct vcons_data *)v;
dp = (struct radeonfb_display *)vd->cookie;
@@ -991,6 +1003,22 @@
#else
return ENODEV;
#endif
+ case WSDISPLAYIO_GETPARAM:
+ param = (struct wsdisplay_param *)d;
+ if (param->param == WSDISPLAYIO_PARAM_BACKLIGHT) {
+ param->min = 0;
+ param->max = RADEONFB_BACKLIGHT_MAX;
+ param->curval = radeonfb_get_backlight(dp);
+ return 0;
+ }
+ return EPASSTHROUGH;
+
+ case WSDISPLAYIO_SETPARAM:
+ param = (struct wsdisplay_param *)d;
+ if (param->param == WSDISPLAYIO_PARAM_BACKLIGHT) {
+ return radeonfb_set_backlight(dp, param->curval);
+ }
+ return EPASSTHROUGH;
default:
return EPASSTHROUGH;
@@ -3302,3 +3330,114 @@
*y = 480;
}
}
+
+
+/* Get the current backlight level for the display. */
+
+static int
+radeonfb_get_backlight(struct radeonfb_display *dp)
+{
+ int s;
+ uint32_t level;
+
+ s = spltty();
+
+ level = radeonfb_get32(dp->rd_softc, RADEON_LVDS_GEN_CNTL);
+ level &= RADEON_LVDS_BL_MOD_LEV_MASK;
+ level >>= RADEON_LVDS_BL_MOD_LEV_SHIFT;
+
+ /*
+ * On some chips, we should negate the backlight level.
+ * XXX Find out on which chips.
+ */
+#ifdef RADEONFB_BACKLIGHT_NEGATED
+ level = RADEONFB_BACKLIGHT_MAX - level;
+#endif /* RADEONFB_BACKLIGHT_NEGATED */
+
+ splx(s);
+
+ return level;
+}
+
+/* Set the backlight to the given level for the display. */
+
+static int
+radeonfb_set_backlight(struct radeonfb_display *dp, int level)
+{
+ struct radeonfb_softc *sc;
+ int rlevel, s;
+ uint32_t lvds;
+
+ s = spltty();
+
+ if (level < 0)
+ level = 0;
+ else if (level >= RADEONFB_BACKLIGHT_MAX)
+ level = RADEONFB_BACKLIGHT_MAX;
+
+ sc = dp->rd_softc;
+
+ /* On some chips, we should negate the backlight level. */
+#ifdef RADEONFB_BACKLIGHT_NEGATED
+ rlevel = RADEONFB_BACKLIGHT_MAX - level;
+#else
+ rlevel = level;
+#endif /* RADEONFB_BACKLIGHT_NEGATED */
+
+ callout_stop(&dp->rd_bl_lvds_co);
+ radeonfb_engine_idle(sc);
+
+ /*
+ * Turn off the display if the backlight is set to 0, since the
+ * display is useless without backlight anyway.
+ */
+ if (level == 0)
+ radeonfb_blank(dp, 1);
+ else if (radeonfb_get_backlight(dp) == 0)
+ radeonfb_blank(dp, 0);
+
+ lvds = radeonfb_get32(sc, RADEON_LVDS_GEN_CNTL);
+ lvds &= ~RADEON_LVDS_DISPLAY_DIS;
+ if (!(lvds & RADEON_LVDS_BLON) || !(lvds & RADEON_LVDS_ON)) {
+ lvds |= dp->rd_bl_lvds_val & RADEON_LVDS_DIGON;
+ lvds |= RADEON_LVDS_BLON | RADEON_LVDS_EN;
+ radeonfb_put32(sc, RADEON_LVDS_GEN_CNTL, lvds);
+ lvds &= ~RADEON_LVDS_BL_MOD_LEV_MASK;
+ lvds |= rlevel << RADEON_LVDS_BL_MOD_LEV_SHIFT;
+ lvds |= RADEON_LVDS_ON;
+ lvds |= dp->rd_bl_lvds_val & RADEON_LVDS_BL_MOD_EN;
+ } else {
+ lvds &= ~RADEON_LVDS_BL_MOD_LEV_MASK;
+ lvds |= rlevel << RADEON_LVDS_BL_MOD_LEV_SHIFT;
+ radeonfb_put32(sc, RADEON_LVDS_GEN_CNTL, lvds);
+ }
+
+ dp->rd_bl_lvds_val &= ~RADEON_LVDS_STATE_MASK;
+ dp->rd_bl_lvds_val |= lvds & RADEON_LVDS_STATE_MASK;
+ /* XXX What is the correct delay? */
+ callout_schedule(&dp->rd_bl_lvds_co, 200 * hz);
+
+ splx(s);
+
+ return 0;
+}
+
+/*
+ * Callout function for delayed operations on the LVDS_GEN_CNTL register.
+ * Set the delayed bits in the register, and clear the stored delayed
+ * value.
+ */
+
+static void radeonfb_lvds_callout(void *arg)
+{
+ struct radeonfb_display *dp = arg;
+ int s;
+
+ s = splhigh();
+
+ radeonfb_mask32(dp->rd_softc, RADEON_LVDS_GEN_CNTL, ~0,
+ dp->rd_bl_lvds_val);
+ dp->rd_bl_lvds_val = 0;
+
+ splx(s);
+}
Index: radeonfbreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/radeonfbreg.h,v
retrieving revision 1.2
diff -u -r1.2 radeonfbreg.h
--- radeonfbreg.h 29 Aug 2006 17:09:33 -0000 1.2
+++ radeonfbreg.h 17 Dec 2006 15:25:03 -0000
@@ -816,6 +816,12 @@
# define RADEON_LVDS_DIGON (1 << 18)
# define RADEON_LVDS_BLON (1 << 19)
# define RADEON_LVDS_SEL_CRTC2 (1 << 23)
+# define RADEON_LVDS_BL_MOD_LEV_MASK 0x0000ff00
+# define RADEON_LVDS_BL_MOD_LEV_SHIFT 8
+# define RADEON_LVDS_BL_MOD_EN (1 << 16)
+# define RADEON_LVDS_STATE_MASK \
+ (RADEON_LVDS_ON | RADEON_LVDS_DISPLAY_DIS | \
+ RADEON_LVDS_BL_MOD_LEV_MASK | RADEON_LVDS_BLON)
#define RADEON_LVDS_PLL_CNTL 0x02d4
# define RADEON_HSYNC_DELAY_SHIFT 28
# define RADEON_HSYNC_DELAY_MASK (0xf << 28)
Index: radeonfbvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/radeonfbvar.h,v
retrieving revision 1.2
diff -u -r1.2 radeonfbvar.h
--- radeonfbvar.h 29 Aug 2006 17:09:33 -0000 1.2
+++ radeonfbvar.h 17 Dec 2006 15:25:03 -0000
@@ -46,6 +46,7 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/device.h>
+#include <sys/callout.h>
#include <dev/pci/pcivar.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsconsio.h>
@@ -172,6 +173,9 @@
int rd_bg; /* background */
int rd_console;
+ struct callout rd_bl_lvds_co; /* delayed lvds operation */
+ uint32_t rd_bl_lvds_val; /* value of delayed lvds */
+
int rd_wsmode;
int rd_ncrtcs;