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);
}
next prev parent 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).