Module Name:    src
Committed By:   macallan
Date:           Thu Apr 23 12:56:40 UTC 2020

Modified Files:
        src/sys/arch/macppc/dev: lmu.c

Log Message:
make this work properly:
- get rid of cargo-culted register assignments, I found the right ones by
  experiment, now both light sensors report sane values
- keyboard brightness seems to max out at 16, act like it
- do what MacOS does and fade keyboard brightness instead of just switching
- add sysctls to configure keyboard brightness and environmental light
  thresholds
- don't poll the chip more often than once a second


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/macppc/dev/lmu.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/macppc/dev/lmu.c
diff -u src/sys/arch/macppc/dev/lmu.c:1.3 src/sys/arch/macppc/dev/lmu.c:1.4
--- src/sys/arch/macppc/dev/lmu.c:1.3	Thu Apr 23 09:47:31 2020
+++ src/sys/arch/macppc/dev/lmu.c	Thu Apr 23 12:56:40 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: lmu.c,v 1.3 2020/04/23 09:47:31 macallan Exp $ */
+/* $NetBSD: lmu.c,v 1.4 2020/04/23 12:56:40 macallan Exp $ */
 
 /*-
  * Copyright (c) 2020 Michael Lorenz
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: lmu.c,v 1.3 2020/04/23 09:47:31 macallan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lmu.c,v 1.4 2020/04/23 12:56:40 macallan Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -40,11 +40,18 @@ __KERNEL_RCSID(0, "$NetBSD: lmu.c,v 1.3 
 #include <sys/bus.h>
 #include <sys/time.h>
 #include <sys/callout.h>
+#include <sys/sysctl.h>
 
 #include <dev/i2c/i2cvar.h>
 
 #include <dev/sysmon/sysmonvar.h>
 
+#ifdef LMU_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF if (0) printf
+#endif
+
 struct lmu_softc {
 	device_t	sc_dev;
 	i2c_tag_t	sc_i2c;
@@ -54,7 +61,9 @@ struct lmu_softc {
 	struct sysmon_envsys *sc_sme;
 	envsys_data_t	sc_sensors[2];
 	callout_t	sc_adjust;
-	int		sc_thresh, sc_hyst, sc_level;
+	int		sc_thresh, sc_hyst, sc_level, sc_target, sc_current;
+	int		sc_lux[2];
+	time_t		sc_last;
 	int		sc_lid_state, sc_video_state;
 };
 
@@ -65,6 +74,8 @@ static void	lmu_sensors_refresh(struct s
 static void	lmu_set_brightness(struct lmu_softc *, int);
 static int	lmu_get_brightness(struct lmu_softc *, int);
 static void	lmu_adjust(void *);
+static int 	lmu_sysctl(SYSCTLFN_ARGS);
+static int 	lmu_sysctl_thresh(SYSCTLFN_ARGS);
 
 CFATTACH_DECL_NEW(lmu, sizeof(struct lmu_softc),
     lmu_match, lmu_attach, NULL, NULL);
@@ -75,6 +86,11 @@ static const struct device_compatible_en
 	{ NULL,			0 }
 };
 
+/* time between polling the light sensors */
+#define LMU_POLL	(hz * 2)
+/* time between updates to keyboard brightness */
+#define LMU_FADE	(hz / 16)
+
 static void
 lmu_lid_open(device_t dev)
 {
@@ -125,11 +141,13 @@ lmu_attach(device_t parent, device_t sel
 	struct lmu_softc *sc = device_private(self);
 	struct i2c_attach_args *ia = aux;
 	envsys_data_t *s;
+	const struct sysctlnode *me;
 
 	sc->sc_dev = self;
 	sc->sc_i2c = ia->ia_tag;
 	sc->sc_addr = ia->ia_addr;
 	sc->sc_node = ia->ia_cookie;
+	sc->sc_last = 0;
 
 	aprint_naive("\n");
 	aprint_normal(": ambient light sensor\n");
@@ -161,7 +179,7 @@ lmu_attach(device_t parent, device_t sel
 	s->state = ENVSYS_SINVALID;
 	s->units = ENVSYS_LUX;
 	strcpy(s->desc, "left");
-	s->private = 2;
+	s->private = 1;
 	sysmon_envsys_sensor_attach(sc->sc_sme, s);
 
 	sysmon_envsys_register(sc->sc_sme);
@@ -169,7 +187,30 @@ lmu_attach(device_t parent, device_t sel
 	/* TODO: make this adjustable via sysctl */
 	sc->sc_thresh = 300;
 	sc->sc_hyst = 30;
-	sc->sc_level = 100;
+	sc->sc_level = 16;
+	sc->sc_target = 0;
+	sc->sc_current = 0;
+
+	sysctl_createv(NULL, 0, NULL, &me,
+		CTLFLAG_READWRITE,
+		CTLTYPE_NODE, "lmu",
+		SYSCTL_DESCR("LMU driver"),
+		NULL, 0, NULL, 0,
+		CTL_HW, CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(NULL, 0, NULL, NULL,
+		CTLFLAG_READWRITE,
+		CTLTYPE_INT, "level",
+		SYSCTL_DESCR("keyboard brightness"),
+		lmu_sysctl, 0, (void *)sc, 0,
+		CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(NULL, 0, NULL, NULL,
+		CTLFLAG_READWRITE,
+		CTLTYPE_INT, "threshold",
+		SYSCTL_DESCR("environmental light threshold"),
+		lmu_sysctl_thresh, 1, (void *)sc, 0,
+		CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
 
 	callout_init(&sc->sc_adjust, 0);
 	callout_setfunc(&sc->sc_adjust, lmu_adjust, sc);
@@ -193,28 +234,38 @@ lmu_sensors_refresh(struct sysmon_envsys
 static int
 lmu_get_brightness(struct lmu_softc *sc, int reg)
 {
-	int error;
-	uint16_t buf;
-	uint8_t cmd = reg;
+	int error, i;
+	uint16_t buf[2];
+	uint8_t cmd = 0;
+
+	if (reg > 1) return -1;
+	if (time_second == sc->sc_last)
+		return sc->sc_lux[reg];
 
 	iic_acquire_bus(sc->sc_i2c, 0);
 	error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
-		sc->sc_addr, &cmd, 1, &buf, 2, 0);
+		sc->sc_addr, &cmd, 1, buf, 4, 0);
 	iic_release_bus(sc->sc_i2c, 0);
 	if (error) return -1;
-	return be16toh(buf);
+	sc->sc_last = time_second;
+
+	for (i = 0; i < 2; i++)
+		sc->sc_lux[i] = be16toh(buf[i]);
+
+	DPRINTF("<%d %04x %04x>", reg, buf[0], buf[1]);
+	
+	return (sc->sc_lux[reg]);
 }
 
 static void
 lmu_set_brightness(struct lmu_softc *sc, int b)
 {
-	int bb;
 	uint8_t cmd[3];
 
 	cmd[0] = 1;
-	bb = b * 255;
-	cmd[1] = (bb & 0xff00) >> 8;
-	cmd[2] =  bb & 0xff;
+
+	cmd[1] = (b & 0xff);
+	cmd[2] = (b & 0xff) >> 8;
 
 	iic_acquire_bus(sc->sc_i2c, 0);
 	iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
@@ -226,18 +277,85 @@ static void
 lmu_adjust(void *cookie)
 {
 	struct lmu_softc *sc = cookie;
-	int left, right, b;
+	int left, right, b, offset;
 
-	left = lmu_get_brightness(sc, 2);
+	left = lmu_get_brightness(sc, 1);
 	right = lmu_get_brightness(sc, 0);
 	b = left > right ? left : right;
 
 	if ((b > (sc->sc_thresh + sc->sc_hyst)) ||
 	   !(sc->sc_lid_state && sc->sc_video_state)) {
-		lmu_set_brightness(sc, 0);
+		sc->sc_target = 0;
 	} else if (b < sc->sc_thresh) {
-		lmu_set_brightness(sc, sc->sc_level);
+		sc->sc_target = sc->sc_level;
+	}
+
+	if (sc->sc_target == sc->sc_current) {
+		/* no update needed, check again later */
+		callout_schedule(&sc->sc_adjust, LMU_POLL);
+		return;
+	}	
+
+
+	offset = ((sc->sc_target - sc->sc_current) > 0) ? 2 : -2;
+	sc->sc_current += offset;
+	if (sc->sc_current > sc->sc_level) sc->sc_current = sc->sc_level;
+	if (sc->sc_current < 0) sc->sc_current = 0;
+
+	DPRINTF("[%d]", sc->sc_current);
+
+	lmu_set_brightness(sc, sc->sc_current);
+
+	if (sc->sc_target == sc->sc_current) {
+		/* no update needed, check again later */
+		callout_schedule(&sc->sc_adjust, LMU_POLL);
+		return;
+	}	
+
+	/* more updates upcoming */
+	callout_schedule(&sc->sc_adjust, LMU_FADE);
+}
+
+static int
+lmu_sysctl(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node = *rnode;
+	struct lmu_softc *sc = node.sysctl_data;
+	int target;
+
+	target = sc->sc_level;
+	node.sysctl_data = &target;
+	if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
+		int new_reg;
+
+		new_reg = *(int *)node.sysctl_data;
+		if (new_reg != sc->sc_target) {
+			sc->sc_level = target;
+			sc->sc_target = target;
+			
+		}
+		return 0;
 	}
+	return EINVAL;
+}
 
-	callout_schedule(&sc->sc_adjust, hz * 2);	
+static int
+lmu_sysctl_thresh(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node = *rnode;
+	struct lmu_softc *sc = node.sysctl_data;
+	int thresh;
+
+	thresh = sc->sc_thresh;
+	node.sysctl_data = &thresh;
+	if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
+		int new_reg;
+
+		new_reg = *(int *)node.sysctl_data;
+		if (new_reg != sc->sc_thresh && new_reg > 0) {
+			sc->sc_thresh = new_reg;
+		}
+		return 0;
+	}
+	return EINVAL;
 }

Reply via email to