shithub: riscv

Download patch

ref: 9a01a24aa53d55dc1f3c4a7c4bee13fe60b6467a
parent: d86a7ed412555192e2000a9a34b3372f380ec3d0
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Aug 17 09:10:21 EDT 2022

aux/wm8960: audio controls fs for MNT Reform

--- a/sys/src/cmd/aux/mkfile
+++ b/sys/src/cmd/aux/mkfile
@@ -50,6 +50,7 @@
 	write\
 	wacom\
 	wikifmt\
+	wm8960\
 	wpa\
 	zerotrunc\
 
--- /dev/null
+++ b/sys/src/cmd/aux/wm8960.c
@@ -1,0 +1,255 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a>b?a:b)
+#define CLAMP(x,a,b) MAX(a,MIN(x,b))
+
+typedef struct Out Out;
+
+enum
+{
+	Dac,
+	Spk,
+	Hp,
+	Nout,
+
+	Ctl = 1,
+	Vol,
+};
+
+struct Out
+{
+	char *name;
+	int volreg;
+	int volmax;
+	void (*toggle)(Out *o, int on);
+	int on;
+	int vol[2];
+};
+
+static char *uid = "audio";
+static int data, reg1a;
+
+static void
+wr(int a, int v)
+{
+	u8int c;
+
+	//fprint(2, "[0x%x] ← 0x%ux = ", a, v & 0x1ff);
+	c = v & 0xff;
+	pwrite(data, &c, 1, a<<1 | ((v>>8)&1));
+}
+
+static void
+dactoggle(Out *o, int on)
+{
+	if(o->on = on)
+		reg1a |= 3<<7;
+	else
+		reg1a &= ~(3<<7);
+	wr(0x1a, reg1a);
+}
+
+static void
+spktoggle(Out *o, int on)
+{
+	if(o->on = on)
+		reg1a |= 3<<3;
+	else
+		reg1a &= ~(3<<3);
+	wr(0x31, (on ? 3 : 0)<<6); /* class D SPK out */
+	wr(0x1a, reg1a);
+}
+
+static void
+hptoggle(Out *o, int on)
+{
+	if(o->on = on)
+		reg1a |= 3<<5;
+	else
+		reg1a &= ~(3<<5);
+	wr(0x1a, reg1a);
+}
+
+static Out out[Nout] =
+{
+	[Dac] = {"master", 0x0a, 0xff, dactoggle, 0},
+	[Spk] = {"spk", 0x28, 0x7f, spktoggle, 0},
+	[Hp] = {"hp", 0x02, 0x7f, hptoggle, 0},
+};
+
+static void
+setvol(Out *o, int l, int r)
+{
+	int zc;
+
+	l = CLAMP(l, 0, 100);
+	r = CLAMP(r, 0, 100);
+
+	o->vol[0] = l;
+	o->vol[1] = r;
+
+	if(l > 0)
+		l += o->volmax - 100;
+	if(r > 0)
+		r += o->volmax - 100;
+
+	zc = o->volmax < 0x80;
+	wr(o->volreg+0, 0<<8 | zc<<7 | l);
+	wr(o->volreg+1, 0<<8 | zc<<7 | r);
+	wr(o->volreg+1, 1<<8 | zc<<7 | r);
+}
+
+static void
+reset(void)
+{
+	Out *o;
+	int i;
+
+	wr(0x0f, 0); /* reset registers to default */
+	wr(0x04, 0); /* sysclk (div 1) derived from mclk; dacdiv=sysclk/256 */
+	wr(0x05, 0<<3); /* unmute DAC */
+	wr(0x06, 1<<3 | 1<<2); /* ramp up DAC volume slowly */
+	wr(0x07, 2); /* i²s, 16-bit words, slave mode */
+	wr(0x08, 7<<6); /* class D divider: sysclk/16 */
+	wr(0x19, 1<<7 | 1<<6); /* Vmid = playback, VREF on */
+	wr(0x22, 1<<8); /* L DAC to mixer */
+	wr(0x25, 1<<8); /* R DAC to mixer */
+	wr(0x2f, 3<<2); /* output mixer on */
+	wr(0x30, 1<<1); /* Tsense on */
+	wr(0x33, 5<<0); /* +5.1dB AC SPK boost - Reform's speakers can be too quiet */
+
+	/* sensible defaults */
+	setvol(&out[Dac], 100, 100);
+	setvol(&out[Spk], 80, 80);
+	setvol(&out[Hp], 65, 65);
+
+	/* enable every output and let the user decide later */
+	for(i = 0, o = out; i < Nout; i++, o++)
+		o->toggle(o, 1);
+}
+
+static void
+fsread(Req *r)
+{
+	char msg[256], *s, *e;
+	Out *o;
+	int i;
+
+	s = msg;
+	e = msg+sizeof(msg);
+	*s = 0;
+	if(r->fid->file->aux == (void*)Ctl){
+		for(i = 0, o = out; i < Nout; i++, o++)
+			s = seprint(s, e, "%s %s\n", o->name, o->on ? "on" : "off");
+	}else if(r->fid->file->aux == (void*)Vol){
+		for(i = 0, o = out; i < Nout; i++, o++)
+			s = seprint(s, e, "%s %d %d\n", o->name, o->vol[0], o->vol[1]);
+	}
+
+	readstr(r, msg);
+	respond(r, nil);
+}
+
+static void
+fswrite(Req *r)
+{
+	int nf, on, i, vl, vr;
+	char msg[256], *f[4];
+	Out *o;
+
+	snprint(msg, sizeof(msg), "%.*s",
+		utfnlen((char*)r->ifcall.data, r->ifcall.count), (char*)r->ifcall.data);
+	if((nf = tokenize(msg, f, nelem(f))) < 2){
+		if(nf == 1 && strcmp(f[0], "reset") == 0){
+			reset();
+			goto Done;
+		}
+Emsg:
+		respond(r, "invalid ctl message");
+		return;
+	}
+	for(i = 0, o = out; i < Nout && strcmp(f[0], o->name) != 0; i++, o++)
+		;
+	if(i >= Nout)
+		goto Emsg;
+
+	if(r->fid->file->aux == (void*)Ctl){
+		if(nf != 2)
+			goto Emsg;
+		if(strcmp(f[1], "on") == 0)
+			on = 1;
+		else if(strcmp(f[1], "off") == 0)
+			on = 0;
+		else if(strcmp(f[1], "toggle") == 0)
+			on = !o->on;
+		else
+			goto Emsg;
+		o->toggle(o, on);
+	}else if(r->fid->file->aux == (void*)Vol){
+		vl = atoi(f[1]);
+		vr = nf < 3 ? vl : atoi(f[2]);
+		setvol(o, vl, vr);
+	}
+
+Done:
+	r->ofcall.count = r->ifcall.count;
+	respond(r, nil);
+}
+
+static Srv fs = {
+	.read = fsread,
+	.write = fswrite,
+};
+
+static void
+usage(void)
+{
+	fprint(2, "usage: aux/wm8960\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	char *mtpt, *srv;
+	int ctl;
+
+	mtpt = "/mnt/wm8960";
+	srv = nil;
+	ARGBEGIN{
+	case 'D':
+		chatty9p = 1;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 's':
+		srv = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if((data = open("#J/i2c3/i2c.1a.data", OWRITE)) < 0)
+		sysfatal("i2c data: %r");
+	if((ctl = open("#J/i2c3/i2c.1a.ctl", OWRITE)) < 0)
+		sysfatal("i2c ctl: %r");
+	fprint(ctl, "subaddress 1\n");
+	fprint(ctl, "size %d\n", 0x38<<1);
+	close(ctl);
+
+	reset();
+
+	fs.tree = alloctree(uid, uid, DMDIR|0555, nil);
+	createfile(fs.tree->root, "audioctl", uid, 0666, (void*)Ctl);
+	createfile(fs.tree->root, "volume", uid, 0666, (void*)Vol);
+
+	postmountsrv(&fs, srv, mtpt, MREPL);
+
+	exits(nil);
+}