Subject: lcd driver
To: None <port-cobalt@netbsd.org>
From: Ted Unangst <tedu@stanford.edu>
List: port-cobalt
Date: 11/23/2001 02:45:25
Hello folks. Fueled by turkey, I present the latest NetBSD
driver. :)
Rebuild your kernel, mknod lcd, and away you go. I've also got a little
tester to play with too. See next mail.
diff -u cobalt.orig/include/lcd.h cobalt/include/lcd.h
--- cobalt.orig/include/lcd.h Fri Nov 23 01:25:35 2001
+++ cobalt/include/lcd.h Thu Nov 22 23:23:29 2001
@@ -0,0 +1,41 @@
+/*
+ * BSD Driver for Cobalt LCD
+ * Copyright 2001 Ted Unangst
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED WITHOUT WARRANTY. USE IT AT YOUR OWN RISK.
+ */
+
+#ifdef _KERNEL
+int lcdopen(dev_t dev, int flags, int fmt, struct proc *p);
+int lcdclose(dev_t dev, int flags, int fmt, struct proc *p);
+int lcdread(dev_t dev, struct uio *uio, int flags);
+int lcdwrite(dev_t dev, struct uio *uio, int flags);
+int lcdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p);
+#endif
+
+#define LCD_OFF 0
+#define LCD_ON 1
+#define LCD_CLEAR 2
+#define LCD_CURSOROFF 3
+#define LCD_CURSORON 4
+#define LCD_BLINKOFF 5
+#define LCD_CURSORLEFT 6
+#define LCD_CURSORRIGHT 7
+#define LCD_LEFT 8
+#define LCD_RIGHT 9
+#define LCD_HOME 10
+#define LCD_READ 11
+#define LCD_WRITE 12
+#define LCD_RESET 14
+#define LCD_CURSORSET 15
+#define LCD_CURSORGET 16
+
diff -u cobalt.orig/conf/GENERIC cobalt/conf/GENERIC
--- cobalt.orig/conf/GENERIC Thu Nov 22 21:00:24 2001
+++ cobalt/conf/GENERIC Thu Nov 22 02:10:02 2001
@@ -111,6 +111,8 @@
pchb* at pci? dev ? function ?
pcib* at pci? dev ? function ?
+lcd0 at mainbus0
+
# PCI serial/parallel interfaces
#puc* at pci? dev ? function ? # PCI "universal" comm. cards
#com* at puc? port ?
diff -u cobalt.orig/conf/files.cobalt cobalt/conf/files.cobalt
--- cobalt.orig/conf/files.cobalt Tue Jan 16 16:07:23 2001
+++ cobalt/conf/files.cobalt Wed Nov 21 21:50:31 2001
@@ -19,9 +19,9 @@
#attach zsc at mainbus with zs_mainbus
#file arch/cobalt/dev/zs_mainbus.c zsc_mainbus needs-flag
-#device panel
-#attach panel at mainbus
-#file arch/cobalt/dev/panel.c panel
+device lcd
+attach lcd at mainbus
+file arch/cobalt/dev/lcd.c lcd
device gt: pcibus
attach gt at mainbus
diff -u cobalt.orig/dev/lcd.c cobalt/dev/lcd.c
--- cobalt.orig/dev/lcd.c Fri Nov 23 01:25:42 2001
+++ cobalt/dev/lcd.c Thu Nov 22 23:23:12 2001
@@ -0,0 +1,272 @@
+/*
+ * BSD Driver for Cobalt LCD
+ * Copyright 2001 Ted Unangst
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED WITHOUT WARRANTY. USE IT AT YOUR OWN RISK.
+ */
+
+/*
+ * Driver notes:
+ * - Doesn't really attach to the system, but there's no need.
+ * - Provides an ioctl to control everything about the LCD, and
+ * also read and write file functions for the text.
+ * - Don't include NUL bytes in the string, as they don't display correctly.
+ * - /dev/lcd can only be opened once at a time.
+ * - The buttons aren't supported yet. They need a userland daemon anyway.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+
+#include <dev/pci/pcivar.h>
+#include "pci.h"
+
+#include <machine/bus.h>
+#include <machine/lcd.h>
+
+int lcd_match(struct device * parent, struct cfdata * match, void *aux);
+void lcd_attach(struct device * parent, struct device * self, void *aux);
+void lcd_read_buffer(void);
+void lcd_write_buffer(void);
+
+struct cfattach lcd_ca = {
+ sizeof(struct device), lcd_match, lcd_attach
+};
+
+struct lcd_softc {
+ struct device sc_dev;
+};
+
+/* Internal state variables */
+int lcd_present = 0;
+int lcd_position;
+char lcd_buffer[80];
+int lcd_open = 0;
+
+/*
+ * This is complete fakery. I don't know where it's
+ * attached, but we can ping it directly. If it's there,
+ * we mark it present, but have to tell the system it's not.
+ */
+int
+lcd_match(struct device * parent, struct cfdata * match, void *aux)
+{
+ if ((((*(volatile u_int32_t *) 0xBF000010) >> 24) & 0xff) == 0x00) {
+ printf("No LCD!\n");
+ } else {
+ printf("lcd0 at mainbus0\n");
+ lcd_present = 1;
+ }
+
+ return 0;
+}
+
+void
+lcd_attach(struct device * parent, struct device * self, void *aux)
+{
+ /* nothing */
+}
+
+/*
+ * Only allow one opening at a time to protect lcd_position.
+ */
+int
+lcdopen(dev_t dev, int flags, int fmt, struct proc * p)
+{
+ if (!lcd_present)
+ return ENXIO;
+ if (lcd_open)
+ return EAGAIN;
+ lcd_open = 1;
+ lcd_position = 0;
+ return (0);
+}
+
+int
+lcdclose(dev_t dev, int flags, int fmt, struct proc * p)
+{
+ lcd_open = 0;
+ return (0);
+}
+
+/* Macros for interfacing with the LCD */
+#define LCD_INST 0xBF000000
+#define LCD_DATA 0xBF000010
+#define LED_ADDR 0xBC000000
+#define LCD_FIRSTROW 0x00000080
+#define LCD_SECONDROW 0x000000C0
+
+#define lcd_write(x) (*(volatile u_int32_t *) LCD_DATA) = ((x) << 24)
+#define lcd_inst(x) (*(volatile u_int32_t *) LCD_INST) = ((x) << 24)
+#define lcd_read() ((*(volatile u_int32_t *) LCD_DATA) >> 24)
+#define led_set(x) (*(volatile unsigned char *) LED_ADDR) = ((char)x)
+
+int lcd_cmds[] = { 0x08, 0x0F, 0x01, 0x0C, 0x0F, 0x0E, 0x10,
+ 0x14, 0x18, 0x1C, 0x02 };
+
+/*
+ * Controls to set various LCD states.
+ */
+int
+lcdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc * p)
+{
+ switch (cmd) {
+ case LCD_OFF:
+ case LCD_ON:
+ case LCD_CLEAR:
+ case LCD_CURSOROFF:
+ case LCD_CURSORON:
+ case LCD_BLINKOFF:
+ case LCD_CURSORLEFT:
+ case LCD_CURSORRIGHT:
+ case LCD_LEFT:
+ case LCD_RIGHT:
+ case LCD_HOME:
+ lcd_inst(lcd_cmds[cmd]);
+ break;
+ case LCD_READ:
+ lcd_read_buffer();
+ memcpy(addr, lcd_buffer, 80);
+ break;
+ case LCD_WRITE:
+ memcpy(lcd_buffer, addr, 80);
+ lcd_write_buffer();
+ break;
+ case LCD_RESET:
+ lcd_inst(0x3F);
+ delay(200);
+ lcd_inst(0x3F);
+ delay(200);
+ lcd_inst(0x3F);
+ delay(200);
+ lcd_inst(0x3F);
+ delay(200);
+ lcd_inst(0x01);
+ delay(200);
+ lcd_inst(0x06);
+ break;
+ case LCD_CURSORSET:
+ lcd_inst(*(unsigned char *)addr | LCD_FIRSTROW);
+ break;
+ case LCD_CURSORGET:
+ *(unsigned char *)addr =
+ ((*(volatile u_int32_t *) LCD_INST) >> 24) | 0x07F;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * We read into the buffer on the first call, otherwise
+ * use our saved lcd_position and copy back from the buffer.
+ */
+int
+lcdread(dev_t dev, struct uio * uio, int flags)
+{
+ u_int32_t rv, len;
+
+ len = uio->uio_resid;
+ if (len == 0 || lcd_position == 80)
+ return 0;
+
+ if (lcd_position == 0)
+ lcd_read_buffer();
+
+ if (len + lcd_position > 80)
+ len = 80 - lcd_position;
+
+ rv = uiomove(lcd_buffer + lcd_position, len, uio);
+ lcd_position += len;
+
+ return rv;
+}
+
+/*
+ * We first always read in the current contents, then overwrite
+ * the buffer with new data. Then the whole buffer is sent back
+ * to the LCD.
+ */
+int
+lcdwrite(dev_t dev, struct uio * uio, int flags)
+{
+ u_int32_t len, rv;
+
+ len = uio->uio_resid;
+ if (len == 0 || lcd_position == 80)
+ return 0;
+
+ lcd_read_buffer();
+
+ if (len + lcd_position > 80)
+ len = 80 - lcd_position;
+
+ rv = uiomove(lcd_buffer + lcd_position, len, uio);
+ lcd_position += len;
+
+ lcd_write_buffer();
+
+ return rv;
+}
+
+/*
+ * Helper functions transfer between the LCD and the buffer
+ */
+void
+lcd_read_buffer(void)
+{
+ u_int32_t i, address;
+
+ for (i = 0, address = LCD_FIRSTROW; i < 40; i++, address++) {
+ delay(200);
+ lcd_inst(address);
+ delay(200);
+ lcd_buffer[i] = lcd_read();
+ }
+ lcd_buffer[39] = '\n';
+ for (address = LCD_SECONDROW; i < 80; i++, address++) {
+ delay(200);
+ lcd_inst(address);
+ delay(200);
+ lcd_buffer[i] = lcd_read();
+ }
+ lcd_buffer[79] = '\0';
+}
+
+void
+lcd_write_buffer(void)
+{
+ int i;
+
+ lcd_inst(0x80);
+ for (i = 0; i < 40; i++) {
+ delay(200);
+ lcd_write(lcd_buffer[i]);
+ }
+ delay(200);
+ lcd_inst(0xC0);
+ for (; i < 80; i++) {
+ delay(200);
+ lcd_write(lcd_buffer[i]);
+ }
+}
diff -u cobalt.orig/cobalt/conf.c cobalt/cobalt/conf.c
--- cobalt.orig/cobalt/conf.c Wed Mar 21 14:25:54 2001
+++ cobalt/cobalt/conf.c Wed Nov 21 23:47:02 2001
@@ -72,6 +72,8 @@
#include "ses.h"
cdev_decl(ses);
#include "ld.h"
+#include <machine/lcd.h>
+cdev_decl(lcd);
#include "i4b.h"
#include "i4bctl.h"
@@ -150,6 +152,7 @@
cdev_i4brbch_init(NI4BRBCH, i4brbch), /* 30: i4b raw b-channel access */
cdev_i4btrc_init(NI4BTRC, i4btrc), /* 31: i4b trace device */
cdev_i4btel_init(NI4BTEL, i4btel), /* 32: i4b phone device */
+ cdev_lcd_init(1, lcd), /* 33: lcd driver */
};
int nchrdev = sizeof(cdevsw) / sizeof(cdevsw[0]);
--
"Bitch set me up."
- M. Barry, Mayor of Washington, DC