9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: Anthony Martin <ality@pbrane.org>
To: 9fans <9fans@9fans.net>
Subject: Re: [9fans] Adjust Thinkpad Screen Brightness
Date: Wed, 7 Jun 2023 15:57:28 -0700	[thread overview]
Message-ID: <ZIELWNCK0leTjQ20@alice> (raw)
In-Reply-To: <FD084FD88540D6B98D42791951071BB2@smtp.pobox.com>

[-- Attachment #1: Type: text/plain, Size: 1161 bytes --]

unobe@cpan.org once said:
> I reviewed the data sheet and the scripts should
> work, but does not.

The above scripts can work for some Intel cards
but probably not for all of them.

I wrote a program fifteen years ago to adjust the
brightness on an Intel MacBook from Linux. There
was a bit of experimentation involved to determine
register values that wouldn't cause problems. I've
attached it below.

Notice how I had to use a mask of 0xfffe instead
of 0xffff to keep the least significant bit zero
and how I had to be strict about maintaining the
maximum backlight level field when reading and
writing the register. I remember working through a
lot of blank and flickering screens before
arriving at something that worked.

I'm not near a machine with an Intel card at the
moment so I can't give you something that works on
Plan 9 but reading this old code may give you some
hints.

Cheers,
  Anthony

------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T4c4247ec1c0429f6-M13bf569fd6da2483ff3c4074
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription

[-- Attachment #2: lcd.c --]
[-- Type: text/plain, Size: 4866 bytes --]

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <pci/pci.h>

typedef unsigned short	ushort;
typedef unsigned int	uint;

// A barebones representation of a PCI device.
typedef struct Pcidev Pcidev;
struct Pcidev {
	ushort     vid;  // PCI vendor ID.
	ushort     did;  // PCI device ID.
	pciaddr_t  bar;  // Physical address of the first BAR.
	size_t     size; // Size of the first BAR.
	char*      addr; // Mapped virtual address of the first BAR.
};

// Macros to read and write 32-bit PCI memory registers.
#define reg32r(p, r)    (*(uint*)((p)->addr+(r)))
#define reg32w(p, r, v)	(*(uint*)((p)->addr+(r)) = (v))

enum {
	// The PCI vendor and device ID for the Intel 945GM graphics chipset.
	Intel  = 0x8086,
	I945GM = 0x27a2,

	// The backlight PWM control register offset.
	Bctl = 0x061254,

	// The backlight duty cycle mask. The LSB must be zero.
	Bmask = 0xfffe,

	// The empirically observed range for valid brightness levels.
	Boff = 0x0000,
	Bmin = 0x003e,
	Bmax = 0x0128,

	// Without an explicit value, we increase or decrease by this
	// quantum so that there are only ten brightness levels.
	Bquant = (Bmax-Bmin)/(10-1),
};

// We don't expose the hardware-centric brightness levels to the user
// but instead shift the range down so it starts at one. These macros
// convert to and fro.
#define B(u)	(((u) == 0) ? Boff : (u)+Bmin-1)
#define U(b)	(((b) == Boff) ? 0 : (b)-Bmin+1)

// The set of operations that can be performed on the backlight.
enum {
	Onop = 1<<0,
	Oset = 1<<1,
	Oadd = 1<<2|Oset,
	Osub = 1<<3|Oset,
};

// The high sixteen bits of the opcode are only used for operations
// that require a numeric argument. These macros ease extraction.
#define OP(o)    ((o)&0xFFFF)
#define OPVAL(v) (((v)&0xFFFF)<<16)

char*	argv0;

void	fatal(char*, ...);
int	pcimatch(Pcidev*, int, int);

int
main(int argc, char *argv[])
{
	int o, fd, max, cur, new;
	char *s, *end;
	Pcidev p;

	// Determine which operation to perform.
	argv0 = argv[0];
	switch(argc){
	default:
		fprintf(stderr, "usage: %s [+-][val]\n", argv0);
		exit(EXIT_FAILURE);
	case 1:
		o = Onop;
		break;
	case 2:
		s = argv[1];
		if(*s == '+' || *s == '-'){
			o = (*s == '+') ? Oadd : Osub;
			s++;
		} else
			o = Oset;
		if(o != Oset && *s == '\0')
			o |= OPVAL(Bquant);
		else {
			o |= OPVAL(strtol(s, &end, 10));
			if(errno != 0 || end == s)
				fatal("invalid number");
		}
		break;
	}

	// Map the GPU device into memory so we can access the backlight register.
	if(pcimatch(&p, Intel, I945GM) < 0)
		fatal("pcimatch failed");
	fd = open("/dev/mem", O_RDWR);
	if(fd < 0)
		fatal("open /dev/mem failed");
	p.addr = mmap(NULL, p.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, p.bar);
	if(p.addr == MAP_FAILED)
		fatal("map failed");
	close(fd);
	max = reg32r(&p, Bctl) >> 16;
	if(max != Bmax)
		fatal("invalid maximum: have %d want %d", max, Bmax);

	// Load the current brightness level and change it, if applicable.
	cur = reg32r(&p, Bctl) & Bmask;
	new = 0;
	switch(OP(o)){
	case Oset:
		new = B(o>>16);
		break;
	case Oadd:
		new = (cur == Boff) ? Bmin : cur + (o>>16);
		break;
	case Osub:
		new = (cur == Bmin) ? Boff : cur - (o>>16);
		break;
	}
	if(o & Oset){
		if(new < 0)
			new = Boff;
		if(new > 0){
			if(new < Bmin)
				new = Bmin;
			if(new > Bmax)
				new = Bmax;
		}
		reg32w(&p, Bctl, (new&Bmask)|(Bmax<<16));
		cur = reg32r(&p, Bctl) & Bmask;
	}

	// Unmap the device and print the current backlight level.
	munmap(p.addr, p.size);
	printf("%d/%d\n", U(cur), U(Bmax));
	return 0;
}

#define nil ((void*)0)

static struct pci_access* pciacc;
static struct pci_dev*    pcilist;

int
pcimatch(Pcidev* p, int vid, int did)
{
	static int pcicfgmode = -1;
	struct pci_access *a;
	struct pci_dev *prev;
	int req;

	if(pcicfgmode == -1){
		a = pci_alloc();
		if(a == nil)
			fatal("pci_alloc failed");
		pci_init(a);
		pci_scan_bus(a);
		pcilist = a->devices;
		pciacc = a;
		pcicfgmode = 0;
	}

	prev = pcilist;
	req = PCI_FILL_IDENT | PCI_FILL_BASES;
	pci_fill_info(prev, req);
	while(prev != nil){
		if((vid == 0 || prev->vendor_id == vid)
		&& (did == 0 || prev->device_id == did))
			break;
		prev = prev->next;
	}
	if(prev == nil)
		return -1;
	p->vid = prev->vendor_id;
	p->did = prev->device_id;

	p->bar = prev->base_addr[0] & PCI_ADDR_MEM_MASK;
	p->size = prev->size[0];
	pci_cleanup(pciacc);
	return 0;
}

void
fatal(char *fmt, ...)
{
	char buf[4096];
	va_list arg;
	int n;

	n = snprintf(buf, sizeof buf, "%s: ", argv0);
	va_start(arg, fmt);
	n += vsnprintf(buf+n, sizeof buf-n, fmt, arg);
	va_end(arg);
	if(errno)
		n += snprintf(buf+n, sizeof buf-n, ": %s", strerror(errno));
	n += snprintf(buf+n, sizeof buf-n, "\n");
	write(2, buf, n);
	exit(EXIT_FAILURE);
}

  reply	other threads:[~2023-06-07 22:58 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-28 16:36 zxcdewqa8
2023-05-28 19:17 ` ori
2023-05-29  2:36   ` Anthony Martin
2023-05-30  4:25     ` zxcdewqa8
2023-06-07  7:01       ` plan6
2023-06-07 21:27         ` mkf9
2023-06-07 21:42           ` unobe
2023-06-07 22:57             ` Anthony Martin [this message]
2023-06-08  7:16               ` plan6
2023-06-08  7:53               ` unobe
2023-06-08  8:12                 ` unobe
2023-06-08  8:08               ` plan6
2023-06-09  6:35                 ` zxcdewqa8
2023-06-09  7:37                   ` unobe
2023-06-09 18:11                   ` ori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZIELWNCK0leTjQ20@alice \
    --to=ality@pbrane.org \
    --cc=9fans@9fans.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).