2006-09-11 23:27:34

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH 2.6.17.13] display: Driver ks0108 and cfag12864b

Miguel Ojeda Sandonis

Adds support for additional "display" devices, like small LCD screens.
Adds support for the ks0108 LCD Controller.
Adds support for the cfag12864b LCD.

Signed-off-by: Miguel Ojeda Sandonis <[email protected]>
---
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
linux-2.6.17.13/Documentation/drivers/display/cfag12864b
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/cfag12864b 2006-09-12
00:10:43.000000000 +0200
@@ -0,0 +1,363 @@
+ ===============================
+ cfag12864b Driver Documentation
+ ===============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+
+
+--------
+0. INDEX
+--------
+
+ 1. DEVICE INFORMATION
+ 2. WIRING
+ 3. USER-SPACE PROGRAMMING
+ 3.1. ioctl and a 128*64 boolean matrix
+ 3.2. Direct writing
+ 4. USEFUL FILES
+ 4.1. cfag12864b.h
+ 4.2. bmpwriter.h
+
+
+
+---------------------
+1. DEVICE INFORMATION
+---------------------
+
+Manufacturer: Crystalfontz
+Webpage: http://www.crystalfontz.com
+Device Webpage: http://www.crystalfontz.com/products/12864b/
+Type: LCD Display
+Width: 128
+Height: 64
+Colors: 2
+Controller: ks0108
+Controllers: 2
+Pages: 8 each controller
+Addresses: 64 each page
+
+
+If you compiled the device as a module, don't remember to
+update udev to get the new device node at /dev.
+
+
+---------
+2. WIRING
+---------
+
+The cfag12864b LCD Display Series don't have a official wiring.
+
+The common wiring is done to the parallel port:
+
+http://www.skippari.net/lcd/sekalaista/crystalfontz_cfag12864B-TMI-V.png
+
+You can get help at Crystalfontz and LCDInfo forums.
+
+
+
+-------------------------
+3. USER-SPACE PROGRAMMING
+-------------------------
+
+Include a copy of the provided header:
+
+ #include "cfag12864b.h"
+
+Open the device for writing, /dev/cfag12864b0:
+
+ int fd = open("/dev/cfag12864b0",O_WRONLY);
+
+Then use simple ioctl calls to control it:
+
+ ioctl(fdisplay,CFAG12864B_IOCOFF); /* Turn off (don't clear) */
+ ioctl(fdisplay,CFAG12864B_IOCON); /* Turn on */
+ ioctl(fdisplay,CFAG12864B_IOCCLEAR); /* Clear the display */
+
+For writing to the display, you have two options:
+
+
+3.1. ioctl & 128*64 boolean matrix
+-------------------------------------------------------
+
+This method is easier, but you have to update the entire display
+each time you want to change it.
+
+Note:
+
+ CFAG12864B_FORMATSIZE ==
+ CFAG12864B_WIDTH * CFAG12864B_HEIGHT ==
+ 128 * 64
+
+Declare the matrix and other one:
+
+ unsigned char MyDrawing[CFAG12864B_WIDTH][CFAG12864B_HEIGHT];
+
+ unsigned char Buffer[CFAG12864B_FORMATSIZE];
+
+Copy the 2d matrix to the buffer , like:
+
+ for(i=0;i<CFAG12864B_WIDTH;++i)
+ for(j=0;j<CFAG12864B_HEIGHT;++j)
+ Buffer[i+j*CFAG12864B_WIDTH]=MyDrawing[i][j];
+
+Call the ioctl:
+
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer);
+
+Voila! Your drawing should appear on the screen.
+
+
+
+3.2. Direct writing
+-------------------
+
+This methods allows you to change each byte of the device,
+so you can achieve a higher update rate.
+
+The device size is 1024 == CFAG12864B_SIZE.
+
+You can write and seek the device. The first 512 bytes write to
+the first k0108 controller (left display half) and the last 512 bytes
+write to the second ks0108 controller (right display half).
+
+Each controller is divided into 8 pages. Each page has 64 bytes.
+
+ Controller 0 Controller 1
+ _________________________
+Page 0 |____________|____________|
+Page 1 |____________|____________|
+Page 2 |____________|____________|
+Page 3 |____________|____________|
+Page 4 |____________|____________|
+Page 5 |____________|____________|
+Page 6 |____________|____________|
+Page 7 |____________|____________|
+ <--- 64 --->
+
+You will understand how the device work executing some commands:
+
+ # echo -n A > /dev/cfag12864b0
+ # echo -n a > /dev/cfag12864b0
+ # echo AAAAAA > /dev/cfag12864b0
+ # echo 000000 > /dev/cfag12864b0
+ # echo Hello world! > /dev/cfag12864b0
+ # echo Hello world! Hello world! > /dev/cfag12864b0
+
+After you understand it, code your functions to change specific bytes.
+
+Use write() and lseek() system calls, like:
+
+ lseek(fdisplay,ipage*CFAG12864B_HEIGHT,SEEK_SET);
+ lseek(fdisplay,icontroller*CFAG12864B_SIZE/2,SEEK_SET);
+
+ write(fdisplay,bufpage,CFAG12864B_HEIGHT);
+ write(fdisplay,bufcontroller,CFAG12864B_SIZE/2);
+ write(fdisplay,bufdisplay,CFAG12864B_SIZE);
+
+
+---------------
+4. USEFUL FILES
+---------------
+
+
+4.1 cfag12864b.h
+----------------
+
+You can use a copy of this header in your user-space programs.
+
+---
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header for user-space apps
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <sys/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_FORMATSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+#define CFAG12864B_SIZE 1024
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+#endif // _CFAG12864B_H_
+---
+
+
+
+4.2 Example BMP writer
+----------------------
+
+You can take ideas from this code. It reads a .bmp file,
+convert it to a boolean [128*64] buffer and then use
+ioctl to display it on the screen.
+
+---
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "cfag12864b.h"
+
+#define BMP_SIZE 1024
+
+union dword
+{
+ unsigned int u32;
+ unsigned char u8[4];
+};
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+
+void BMP2Format(
+ unsigned char _Src[BMP_SIZE],
+ unsigned char _Dest[CFAG12864B_FORMATSIZE])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Bits = 8;
+
+ unsigned int Y,X,Bit;
+
+ for(Y=0; Y<Height; ++Y)
+ for(X=0; X<Width/Bits; ++X)
+ for(Bit=0; Bit<Bits; ++Bit)
+ _Dest[X*Bits+Bit+(Height-Y-1)*Width] =
+ _Src[Y*Width/Bits+X]&Bit(Bits-Bit-1)?0:1;
+}
+
+int main(int argc, char * argv[])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Size = CFAG12864B_SIZE;
+ const unsigned int BPP = 1;
+ const unsigned int HeaderSize = 0x3E;
+ const unsigned int BMPSize = BMP_SIZE;
+
+ unsigned char c;
+ unsigned int i,j;
+ union dword n;
+
+ unsigned char Buffer_BMP[BMP_SIZE];
+ unsigned char Buffer_Matrix[CFAG12864B_FORMATSIZE];
+
+ int fdisplay;
+ FILE * fbmp;
+
+ // Check args
+ if(argc!=3) {
+ printf("%s: Bad number of arguments. Expected 3\n",
+ argv[0]);
+ return -1;
+ }
+
+ // Open file
+ fbmp = fopen(argv[2],"rb");
+ if(fbmp==NULL) {
+ printf("%s: Can't open %s\n",argv[0], argv[2]);
+ return -2;
+ }
+
+ // Check file size
+ fseek(fbmp,0,SEEK_END);
+ i=ftell(fbmp);
+ if(i!=HeaderSize+Size) {
+ printf("%s: Bad file size. %i instead of %i\n",
+ argv[0], i, HeaderSize+Size);
+ fclose(fbmp);
+ return -3;
+ }
+
+ // Check both magic BMP bytes
+ fseek(fbmp,0,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!='B') {
+ printf("%s: Bad first magic byte. '%c' instead of 'B'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -4;
+ }
+ c = fgetc(fbmp);
+ if(c!='M') {
+ printf("%s: Bad second magic byte. '%c' instead of 'M'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -5;
+ }
+
+ // Check this is a 128x64 1-bpp BMP file
+ fseek(fbmp,0x12,SEEK_SET);
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Width) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Width);
+ fclose(fbmp);
+ return -6;
+ }
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Height) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Height);
+ fclose(fbmp);
+ return -7;
+ }
+ fseek(fbmp,0x1C,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!=BPP) {
+ printf("%s: Bad bpp. %i instead of %i\n",
+ argv[0], c, BPP);
+ fclose(fbmp);
+ return -8;
+ }
+
+ // Get bitmap data
+ fseek(fbmp,0x3E,SEEK_SET);
+ for(i=0; i<BMPSize; ++i)
+ Buffer_BMP[i]=fgetc(fbmp);
+ fclose(fbmp);
+
+ // Transform BMP data to 2D matrix
+ BMP2Format(Buffer_BMP,Buffer_Matrix);
+
+ // Open file
+ fdisplay = open(argv[1],O_WRONLY);
+ if(fdisplay < 0) {
+ printf("%s: Can't open %s\n", argv[0], argv[1]);
+ return -9;
+ }
+
+ // Send matrix
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer_Matrix);
+
+ // Close file
+ close(fdisplay);
+
+ return 0;
+}
+---
+
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/display
linux-2.6.17.13/Documentation/drivers/display/display
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/display 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/display 2006-09-12
00:24:39.000000000 +0200
@@ -0,0 +1,37 @@
+ =============================
+ Display Drivers Documentation
+ =============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+--------
+0. INDEX
+--------
+
+ 1. NEW DISPLAY DRIVERS
+ 2. GENERAL TIPS
+
+
+
+----------------------
+1. NEW DISPLAY DRIVERS
+----------------------
+
+Feel free to send me new display drivers. I will try to do my best.
+
+If you don't get any answer, send your patch directly to the linux-kernel ml.
+
+
+
+---------------
+2. GENERAL TIPS
+---------------
+
+- Divide your driver into the controller driver, like ks0108,
+ and the specific LCD display series driver, like cfag12864b.
+
+- Claim for your IO ports in the controller driver.
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt
linux-2.6.17.13/Documentation/ioctl-number.txt
--- linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt 2006-09-09
05:23:25.000000000 +0200
+++ linux-2.6.17.13/Documentation/ioctl-number.txt 2006-09-12
00:36:48.000000000 +0200
@@ -190,3 +190,5 @@ Code Seq# Include File Comments
<mailto:[email protected]>
0xF3 00-3F video/sisfb.h sisfb (in development)
<mailto:[email protected]>
+0xFF 00-1F cfag12864b LCD Display linux/cfag12864b.h
+ <mailto:[email protected]>
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/CREDITS linux-2.6.17.13/CREDITS
--- linux-2.6.17.13-vanilla/CREDITS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/CREDITS 2006-09-12 00:16:06.000000000 +0200
@@ -2534,6 +2534,14 @@ S: Subiaco, 6008
S: Perth, Western Australia
S: Australia

+N: Miguel Ojeda Sandonis
+E: [email protected]
+D: Author: LCD Display Drivers (ks0108, cfag12864b)
+D: Maintainer: LCD Display Drivers Tree (drivers/display/*)
+S: C/ Mieses 20, 9-B
+S: Valladolid 47009
+S: Spain
+
N: Greg Page
E: [email protected]
D: IPX development and support
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c
linux-2.6.17.13/drivers/display/cfag12864b.c
--- linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/cfag12864b.c 2006-09-11
23:48:59.000000000 +0200
@@ -0,0 +1,585 @@
+/*
+ * Filename: cfag12864b.c
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver
+ * License: GPL
+ * Depends: display ks0108
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/display.h>
+#include <linux/ks0108.h>
+#include <linux/cfag12864b.h>
+#include <asm/uaccess.h>
+
+#define NAME "cfag12864b"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Device
+//
+
+static const unsigned int FirstMinor = 0;
+static const unsigned int nDevices = 1;
+static const char * Name = NAME;
+
+static int Major;
+static dev_t FirstDevice;
+
+struct cfag12864b
+{
+ int Minor;
+ dev_t Device;
+ struct cdev CharDevice;
+};
+
+static struct cfag12864b * Devices;
+
+
+
+//
+// cfag12864b Commands
+//
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+#define NoBit(n) ((unsigned char)(~Bit(n)))
+static const unsigned int Bits = 8;
+
+static const unsigned int Width = CFAG12864B_WIDTH;
+static const unsigned int Height = CFAG12864B_HEIGHT;
+static const unsigned int MatrixSize = CFAG12864B_MATRIXSIZE;
+static const unsigned int Controllers = CFAG12864B_CONTROLLERS;
+static const unsigned int Pages = CFAG12864B_PAGES;
+static const unsigned int Addresses = CFAG12864B_ADDRESSES;
+static const unsigned int Size = CFAG12864B_SIZE;
+
+static unsigned char State = 0;
+
+void cfag12864b_Set(void)
+{
+ ks0108_WriteControl(State ^ (Bit(3) | Bit(1) | Bit(0)) );
+}
+
+void cfag12864b_E(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(0);
+ else
+ State &= NoBit(0);
+ cfag12864b_Set();
+}
+
+void cfag12864b_CS1(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(2);
+ else
+ State &= NoBit(2);
+ cfag12864b_Set();
+}
+
+void cfag12864b_CS2(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(1);
+ else
+ State &= NoBit(1);
+ cfag12864b_Set();
+}
+
+void cfag12864b_DI(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(3);
+ else
+ State &= NoBit(3);
+ cfag12864b_Set();
+}
+
+void cfag12864b_FirstController(unsigned char _State)
+{
+ if(_State)
+ cfag12864b_CS1(0);
+ else
+ cfag12864b_CS1(1);
+}
+
+void cfag12864b_SecondController(unsigned char _State)
+{
+ if(_State)
+ cfag12864b_CS2(0);
+ else
+ cfag12864b_CS2(1);
+}
+
+void cfag12864b_Controllers(unsigned char _First, unsigned char _Second)
+{
+ cfag12864b_FirstController(_First);
+ cfag12864b_SecondController(_Second);
+}
+
+void cfag12864b_Controller(unsigned char _Which)
+{
+ if(_Which==0)
+ cfag12864b_Controllers(1,0);
+ else if(_Which==1)
+ cfag12864b_Controllers(0,1);
+}
+
+void cfag12864b_DisplayState(unsigned char _State)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_DisplayState(_State);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_Address(unsigned char _Address)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_Address(_Address);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_Page(unsigned char _Page)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_Page(_Page);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_StartLine(unsigned char _StartLine)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_StartLine(_StartLine);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_WriteByte(unsigned char _Byte)
+{
+ cfag12864b_DI(1);
+ cfag12864b_E(1);
+ ks0108_WriteData(_Byte);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_NOP(void)
+{
+ cfag12864b_StartLine(0);
+}
+
+
+
+//
+// Auxiliar
+//
+
+void NormalizeOffset(unsigned int * _Offset)
+{
+ if(*_Offset>=Pages*Addresses)
+ *_Offset-=Pages*Addresses;
+}
+
+unsigned char GetAddress(unsigned int _Offset)
+{
+ NormalizeOffset(&_Offset);
+ return _Offset%Addresses;
+}
+
+unsigned char GetController(unsigned int _Offset)
+{
+ if(_Offset<Pages*Addresses)
+ return 0;
+ return 1;
+}
+
+unsigned char GetPage(unsigned int _Offset)
+{
+ NormalizeOffset(&_Offset);
+ return _Offset/Addresses;
+}
+
+
+
+//
+// cfag12864b Exported Commands
+//
+
+void cfag12864b_On(void)
+{
+ cfag12864b_Controllers(1,1);
+ cfag12864b_DisplayState(1);
+}
+
+void cfag12864b_Off(void)
+{
+ cfag12864b_Controllers(1,1);
+ cfag12864b_DisplayState(0);
+}
+
+void cfag12864b_Clear(void)
+{
+ unsigned char Page,Address;
+
+ cfag12864b_Controllers(1,1);
+ for(Page=0; Page<Pages; ++Page) {
+ cfag12864b_Page(Page);
+ cfag12864b_Address(0);
+ for(Address=0; Address<Addresses; ++Address)
+ cfag12864b_WriteByte(0x00);
+ }
+}
+
+void cfag12864b_Write(
+ unsigned short _Offset,
+ unsigned char * _Buffer,
+ unsigned short _Count)
+{
+ unsigned short i;
+
+ // Invalid values: They get updated at the first cycle
+ unsigned char Controller = 0xFF;
+ unsigned char Page = 0xFF;
+ unsigned char Address = 0xFF;
+
+ unsigned char tmpController, tmpPage, tmpAddress;
+
+ for(i=0; i<_Count; ++i, ++_Offset, ++Address) {
+ tmpController = GetController(_Offset);
+ tmpPage = GetPage(_Offset);
+ tmpAddress = GetAddress(_Offset);
+
+ if(Controller != tmpController) {
+ Controller = tmpController;
+ cfag12864b_Controller(Controller);
+ cfag12864b_NOP();
+ }
+ if(Page != tmpPage) {
+ Page = tmpPage;
+ cfag12864b_Page(Page);
+ cfag12864b_NOP();
+ }
+
+ /*if(Address != tmpAddress) {
+ Address = tmpAddress;
+ cfag12864b_Address(Address);
+ cfag12864b_NOP();
+ }*/
+
+ /*if(tmpController == 0) {
+ if(Address != tmpAddress) {
+ Address = tmpAddress;
+ cfag12864b_Address(Address);
+ }
+ }
+ else {
+ cfag12864b_Address(tmpAddress);
+ cfag12864b_NOP();
+ }*/
+
+ // Safe method, still quick
+ cfag12864b_Address(tmpAddress);
+ cfag12864b_NOP();
+
+ // Dummy
+ cfag12864b_NOP();
+
+ cfag12864b_WriteByte(_Buffer[i]);
+ }
+}
+
+void cfag12864b_Format(unsigned char * _Src)
+{
+ unsigned short Controller,Page,Address,Bit;
+ unsigned char * Dest = kmalloc(sizeof(unsigned char)*Size,GFP_KERNEL);
+
+ if(Dest == NULL) {
+ printk(PRINTK_PREFIX "Format: ERROR: "
+ "kmalloc\n");
+ return;
+ }
+
+ for(Controller=0; Controller<Controllers; ++Controller)
+ for(Page=0; Page<Pages; ++Page)
+ for(Address=0; Address<Addresses; ++Address) {
+ Dest[(Controller*Pages+Page)*Addresses+Address]=0;
+ for(Bit=0; Bit<Bits; ++Bit)
+ if(_Src[Controller*Addresses+Address+(Page*Bits+Bit)*Width])
+ Dest[(Controller*Pages+Page)*Addresses+Address]|=Bit(Bit);
+ }
+
+ cfag12864b_Write(0,Dest,Size);
+
+ kfree(Dest);
+}
+
+EXPORT_SYMBOL_GPL(cfag12864b_On);
+EXPORT_SYMBOL_GPL(cfag12864b_Off);
+EXPORT_SYMBOL_GPL(cfag12864b_Clear);
+EXPORT_SYMBOL_GPL(cfag12864b_Write);
+EXPORT_SYMBOL_GPL(cfag12864b_Format);
+
+
+//
+// Fops
+//
+
+int Open(
+ struct inode * _Inode,
+ struct file * _File)
+{
+ _File->private_data = container_of(_Inode->i_cdev, struct
cfag12864b, CharDevice);
+
+ return 0;
+}
+
+loff_t Seek(
+ struct file * _File,
+ loff_t _Offset,
+ int _Whence)
+{
+ loff_t New;
+
+ switch(_Whence) {
+ case 0: // SEEK_SET
+ New = _Offset;
+ break;
+ case 1: // SEEK_CUR
+ New = _File->f_pos + _Offset;
+ break;
+ case 2: // SEEK_END
+ New = Size + _Offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if(New < 0)
+ return -EINVAL;
+ _File->f_pos = New;
+ return New;
+}
+
+
+ssize_t Write(
+ struct file * _File,
+ const char __user * _Buffer,
+ size_t _Count,
+ loff_t * _Offset)
+{
+ int Result;
+ char * Buffer;
+
+ if(*_Offset>Size)
+ return 0;
+ if(*_Offset+_Count>Size)
+ _Count=Size-*_Offset;
+
+ Buffer = kmalloc(_Count,GFP_KERNEL);
+ if(Buffer == NULL) {
+ printk(PRINTK_PREFIX "FOP Write: ERROR: "
+ "kmalloc\n");
+ return -ENOMEM;
+ }
+
+ Result = copy_from_user(Buffer, _Buffer, _Count);
+ if(Result != 0) {
+ printk(PRINTK_PREFIX "FOP Write: ERROR: "
+ "copy_from_user\n");
+ Result = -EFAULT;
+ goto out;
+ }
+
+ cfag12864b_Write(*_Offset, Buffer, _Count);
+
+ *_Offset += _Count;
+ Result = _Count;
+
+out:
+ kfree(Buffer);
+ return Result;
+}
+
+int IOCtl_Format(unsigned long _Arg)
+{
+ int Result,Return = -ENOTTY;
+
+ unsigned char * IOCFormatSrc;
+
+ // Alloc src buffer
+ IOCFormatSrc = kmalloc(sizeof(unsigned char)*Width*Height,GFP_KERNEL);
+ if(IOCFormatSrc == NULL) {
+ printk(PRINTK_PREFIX "FOP IOCtl: ERROR: "
+ "IOC FormatWrite: "
+ "kmalloc\n");
+ goto out;
+ }
+
+ // Copy src buffer from user
+ Result = copy_from_user(
+ IOCFormatSrc,
+ (void __user *)_Arg,
+ sizeof(unsigned char)*Width*Height);
+ if(Result != 0) {
+ printk(PRINTK_PREFIX "FOP IOCtl: ERROR: "
+ "IOC FormatWrite: "
+ "copy_from_user(IOCFormatSrc,...\n");
+ goto allocsrc;
+ }
+
+ cfag12864b_Format(IOCFormatSrc);
+
+ Return = 0;
+
+allocsrc:
+ kfree(IOCFormatSrc);
+out:
+ return Return;
+}
+
+int IOCtl(
+ struct inode * _Inode,
+ struct file * _File,
+ unsigned int _Cmd,
+ unsigned long _Arg)
+{
+ if(_IOC_TYPE(_Cmd) != CFAG12864B_IOC_MAGIC)
+ return -ENOTTY;
+ if(_IOC_NR(_Cmd) > CFAG12864B_IOC_MAXNR)
+ return -ENOTTY;
+
+ switch(_Cmd) {
+ case CFAG12864B_IOCON:
+ cfag12864b_On();
+ break;
+ case CFAG12864B_IOCOFF:
+ cfag12864b_Off();
+ break;
+ case CFAG12864B_IOCCLEAR:
+ cfag12864b_Clear();
+ break;
+ case CFAG12864B_IOCFORMAT:
+ return IOCtl_Format(_Arg);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+
+
+struct file_operations Fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = Seek,
+ .write = Write,
+ .ioctl = IOCtl,
+ .open = Open,
+};
+
+
+
+
+
+
+
+
+
+//
+// Module Init & Exit
+//
+
+int cfag12864b_init(void)
+{
+ unsigned int i;
+ int Result;
+
+ printk(PRINTK_PREFIX "Init... ");
+
+ Result = alloc_chrdev_region(&FirstDevice, FirstMinor, nDevices, Name);
+ if(Result < 0) {
+ printk("ERROR - alloc_chrdev_region\n");
+ return Result;
+ }
+ Major = MAJOR(FirstDevice);
+
+ Devices = kmalloc(nDevices * sizeof(struct cfag12864b), GFP_KERNEL);
+ if(Devices == NULL) {
+ printk("ERROR - kmalloc\n");
+ return -ENOMEM;
+ }
+ memset(Devices, 0, nDevices * sizeof(struct cfag12864b));
+
+ for(i=0; i<nDevices; ++i) {
+ Devices[i].Minor = FirstMinor+i;
+ Devices[i].Device = MKDEV(Major,Devices[i].Minor);
+
+ cdev_init(&(Devices[i].CharDevice), &Fops);
+ Devices[i].CharDevice.owner = THIS_MODULE;
+ Devices[i].CharDevice.ops = &Fops;
+ Result = cdev_add(&(Devices[i].CharDevice),
+ Devices[i].Device, 1);
+ if(Result < 0) {
+ printk("ERROR - cdev_add\n");
+ kfree(Devices);
+ return Result;
+ }
+
+ class_device_create(
+ display_class,NULL,MKDEV(Major,Devices[i].Minor),
+ NULL,"cfag12864b%d",Devices[i].Minor);
+ }
+
+
+ cfag12864b_Clear();
+ cfag12864b_On();
+
+ printk("Done\n");
+
+ return 0;
+}
+
+void cfag12864b_exit(void)
+{
+ unsigned int i;
+
+ printk(PRINTK_PREFIX "Exit... ");
+
+ cfag12864b_Off();
+
+ if(Devices != NULL) {
+ for(i=0; i<nDevices; ++i) {
+ class_device_destroy(display_class,MKDEV(Major,Devices[i].Minor));
+ cdev_del(&(Devices[i].CharDevice));
+ }
+ kfree(Devices);
+ }
+
+ unregister_chrdev_region(FirstDevice, nDevices);
+
+ printk("Done\n");
+}
+
+module_init(cfag12864b_init);
+module_exit(cfag12864b_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("cfag12864b");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/display.c
linux-2.6.17.13/drivers/display/display.c
--- linux-2.6.17.13-vanilla/drivers/display/display.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/display.c 2006-09-11
23:53:22.000000000 +0200
@@ -0,0 +1,60 @@
+/*
+ * Filename: display.c
+ * Version: 0.1.0
+ * Description: Display Class
+ * License: GPL
+ * Depends: -
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/display.h>
+
+#define NAME "display"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+static char * Name = NAME;
+
+//
+// Exported Display Data
+//
+
+struct class * display_class;
+EXPORT_SYMBOL_GPL(display_class);
+
+
+//
+// Module Init & Exit
+//
+
+int display_init(void)
+{
+ display_class = class_create(THIS_MODULE,Name);
+ if(IS_ERR(display_class)) {
+ printk(PRINTK_PREFIX "ERROR: class_simple_create\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void display_exit(void)
+{
+ class_destroy(display_class);
+}
+
+module_init(display_init);
+module_exit(display_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("display");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Kconfig
linux-2.6.17.13/drivers/display/Kconfig
--- linux-2.6.17.13-vanilla/drivers/display/Kconfig 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Kconfig 2006-09-12 00:04:14.000000000 +0200
@@ -0,0 +1,61 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+# Display drivers configuration.
+#
+
+menu "Display support"
+
+config DISPLAY
+ tristate "Display support"
+ default n
+ ---help---
+ If you have an extra display, like a small LCD screen, say Y.
+
+ To compile this as a module, choose M here:
+ module will be called display.
+
+ If unsure, say N.
+
+comment "Parallel port dependent:"
+
+config KS0108
+ tristate "KS0108 LCD Controller"
+ depends on DISPLAY && PARPORT
+ default n
+ ---help---
+ If you have a LCD display controlled by one or more KS0108
+ controllers, say Y. You will need also another more specific
+ driver for your LCD.
+
+ Depends on Parallel Port support. If you say Y at
+ parport, you will be able to compile this as a module (M)
+ and built-in as well (Y). If you said M at parport,
+ you will be able only to compile this as a module (M).
+
+ To compile this as a module, choose M here:
+ module will be called ks0108.
+
+ If unsure, say N.
+
+config CFAG12864B
+ tristate "CFAG12864B LCD Display"
+ depends on KS0108
+ default n
+ ---help---
+ If you have a Crystalfontz 128x64 2-color LCD display,
+ cfag12864b Series, say Y. You also need the ks0108 LCD
+ Controller driver.
+
+ For help about how to wire your LCD to the parallel port,
+ check this image: http://www.skippari.net/lcd/sekalaista
+ /crystalfontz_cfag12864B-TMI-V.png
+
+ To compile this as a module, choose M here:
+ module will be called cfag12864b.
+
+ If unsure, say N.
+
+endmenu
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/ks0108.c
linux-2.6.17.13/drivers/display/ks0108.c
--- linux-2.6.17.13-vanilla/drivers/display/ks0108.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/ks0108.c 2006-09-11 23:49:15.000000000 +0200
@@ -0,0 +1,177 @@
+/*
+ * Filename: ks0108.c
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver
+ * License: GPL
+ * Depends: parport
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/parport.h>
+#include <linux/ks0108.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+#define NAME "ks0108"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Module
+//
+
+static unsigned int Port = 0x378;
+module_param(Port,uint,S_IRUGO);
+
+
+
+//
+// Device
+//
+
+static const char * Name = NAME;
+
+static struct parport * Parport;
+static struct pardevice * Pardevice;
+
+
+
+//
+// ks0108 Exported Commands
+//
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+
+void ks0108_WriteData(unsigned char _Byte)
+{
+ parport_write_data(Parport,_Byte);
+}
+
+void ks0108_WriteControl(unsigned char _Byte)
+{
+ const unsigned int ECycleDelay = 1;
+ udelay(ECycleDelay);
+ parport_write_control(Parport,_Byte);
+}
+
+void ks0108_DisplayState(unsigned char _State)
+{
+ unsigned char Command = Bit(1) | Bit(2) | Bit(3) | Bit(4) | Bit(5);
+ if(_State)
+ Command |= Bit(0);
+ ks0108_WriteData(Command);
+}
+
+void ks0108_StartLine(unsigned char _StartLine)
+{
+ const unsigned char MaxStartLine = 63;
+ unsigned char Command = Bit(6) | Bit(7);
+ if(_StartLine>MaxStartLine)
+ _StartLine=MaxStartLine;
+ Command |= _StartLine;
+ ks0108_WriteData(Command);
+}
+
+void ks0108_Address(unsigned char _Address)
+{
+ const unsigned char MaxAddress = 63;
+ unsigned char Command = Bit(6);
+ if(_Address>MaxAddress)
+ _Address=MaxAddress;
+ Command |= _Address;
+ ks0108_WriteData(Command);
+}
+
+void ks0108_Page(unsigned char _Page)
+{
+ const unsigned char MaxPage = 7;
+ unsigned char Command = Bit(3) | Bit(4) | Bit(5) | Bit(7);
+ if(_Page>MaxPage)
+ _Page=MaxPage;
+ Command |= _Page;
+ ks0108_WriteData(Command);
+}
+
+
+EXPORT_SYMBOL_GPL(ks0108_WriteData);
+EXPORT_SYMBOL_GPL(ks0108_WriteControl);
+EXPORT_SYMBOL_GPL(ks0108_DisplayState);
+EXPORT_SYMBOL_GPL(ks0108_StartLine);
+EXPORT_SYMBOL_GPL(ks0108_Address);
+EXPORT_SYMBOL_GPL(ks0108_Page);
+
+
+
+
+//
+// Module Init & Exit
+//
+
+int ks0108_init(void)
+{
+ int Return = -EINVAL;
+
+ printk(PRINTK_PREFIX "Init... ");
+
+ Parport = parport_find_base(Port);
+ if(Parport == NULL) {
+ printk("ERROR - parport_find_base\n");
+ Return = -EFAULT;
+ goto none;
+ }
+
+ Pardevice = parport_register_device(
+ Parport,Name,
+ NULL,NULL,NULL,
+ PARPORT_DEV_EXCL,NULL);
+ if(Pardevice == NULL) {
+ printk("ERROR: parport_register_device\n");
+ Return = -EFAULT;
+ goto none;
+ }
+
+ if(parport_claim(Pardevice) != 0) {
+ printk("ERROR: parport_claim\n");
+ Return = -EFAULT;
+ goto registered;
+ }
+
+ printk("Done\n");
+ return 0;
+
+registered:
+ parport_unregister_device(Pardevice);
+
+none:
+ return Return;
+}
+
+void ks0108_exit(void)
+{
+ printk(PRINTK_PREFIX "Exit... ");
+
+ parport_release(Pardevice);
+
+ parport_unregister_device(Pardevice);
+
+ printk("Done\n");
+}
+
+module_init(ks0108_init);
+module_exit(ks0108_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("ks0108");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Makefile
linux-2.6.17.13/drivers/display/Makefile
--- linux-2.6.17.13-vanilla/drivers/display/Makefile 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Makefile 2006-09-11 18:00:05.000000000 +0200
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel Display device drivers.
+#
+
+obj-$(CONFIG_DISPLAY) += display.o
+obj-$(CONFIG_KS0108) += ks0108.o
+obj-$(CONFIG_CFAG12864B) += cfag12864b.o
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Kconfig
linux-2.6.17.13/drivers/Kconfig
--- linux-2.6.17.13-vanilla/drivers/Kconfig 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Kconfig 2006-09-11 19:04:53.000000000 +0200
@@ -72,4 +72,8 @@ source "drivers/edac/Kconfig"

source "drivers/rtc/Kconfig"

+# parport before display - some displays depend on it.
+
+source "drivers/display/Kconfig"
+
endmenu
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Makefile
linux-2.6.17.13/drivers/Makefile
--- linux-2.6.17.13-vanilla/drivers/Makefile 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Makefile 2006-09-11 19:15:50.000000000 +0200
@@ -74,3 +74,4 @@ obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
+obj-$(CONFIG_DISPLAY) += display/
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/cfag12864b.h
linux-2.6.17.13/include/linux/cfag12864b.h
--- linux-2.6.17.13-vanilla/include/linux/cfag12864b.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/cfag12864b.h 2006-09-11
17:32:19.000000000 +0200
@@ -0,0 +1,45 @@
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <linux/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_MATRIXSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+
+#define CFAG12864B_CONTROLLERS 2
+#define CFAG12864B_PAGES 8
+#define CFAG12864B_ADDRESSES 64
+#define CFAG12864B_SIZE CFAG12864B_CONTROLLERS * \
+ CFAG12864B_PAGES * \
+ CFAG12864B_ADDRESSES
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+#define CFAG12864B_IOC_MAXNR 0x03
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+extern void cfag12864b_On(void);
+extern void cfag12864b_Off(void);
+extern void cfag12864b_Clear(void);
+extern void cfag12864b_Write(
+ unsigned short Offset,
+ unsigned char * Buffer,
+ unsigned short Count);
+extern void cfag12864b_Format(unsigned char * Src);
+
+#endif // _CFAG12864B_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/display.h
linux-2.6.17.13/include/linux/display.h
--- linux-2.6.17.13-vanilla/include/linux/display.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/display.h 2006-09-11 19:25:44.000000000 +0200
@@ -0,0 +1,19 @@
+/*
+ * Filename: display.h
+ * Version: 0.1.0
+ * Description: Display Class Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include <linux/device.h>
+
+extern struct class * display_class;
+
+#endif // _DISPLAY_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/ks0108.h
linux-2.6.17.13/include/linux/ks0108.h
--- linux-2.6.17.13-vanilla/include/linux/ks0108.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/ks0108.h 2006-09-11 23:27:26.000000000 +0200
@@ -0,0 +1,22 @@
+/*
+ * Filename: ks0108.h
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _KS0108_H_
+#define _KS0108_H_
+
+extern void ks0108_WriteData(unsigned char Byte);
+extern void ks0108_WriteControl(unsigned char Byte);
+extern void ks0108_DisplayState(unsigned char State);
+extern void ks0108_StartLine(unsigned char StartLine);
+extern void ks0108_Address(unsigned char Address);
+extern void ks0108_Page(unsigned char Page);
+
+#endif // _KS0108_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/MAINTAINERS linux-2.6.17.13/MAINTAINERS
--- linux-2.6.17.13-vanilla/MAINTAINERS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/MAINTAINERS 2006-09-12 00:17:51.000000000 +0200
@@ -1640,6 +1640,11 @@ M: [email protected]
L: [email protected]
S: Maintained

+DISPLAY DRIVERS
+P: Miguel Ojeda Sandonis
+M: [email protected]
+S: Maintained
+
LED SUBSYSTEM
P: Richard Purdie
M: [email protected]


2006-09-11 23:54:20

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH 1/2] display: Driver ks0108 and cfag12864b

Miguel Ojeda Sandonis

Adds support for additional "display" devices, like small LCD screens.
Adds support for the ks0108 LCD Controller.
Adds support for the cfag12864b LCD.

Signed-off-by: Miguel Ojeda Sandonis <[email protected]>
---
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
linux-2.6.17.13/Documentation/drivers/display/cfag12864b
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/cfag12864b 2006-09-12
00:10:43.000000000 +0200
@@ -0,0 +1,363 @@
+ ===============================
+ cfag12864b Driver Documentation
+ ===============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+
+
+--------
+0. INDEX
+--------
+
+ 1. DEVICE INFORMATION
+ 2. WIRING
+ 3. USER-SPACE PROGRAMMING
+ 3.1. ioctl and a 128*64 boolean matrix
+ 3.2. Direct writing
+ 4. USEFUL FILES
+ 4.1. cfag12864b.h
+ 4.2. bmpwriter.h
+
+
+
+---------------------
+1. DEVICE INFORMATION
+---------------------
+
+Manufacturer: Crystalfontz
+Webpage: http://www.crystalfontz.com
+Device Webpage: http://www.crystalfontz.com/products/12864b/
+Type: LCD Display
+Width: 128
+Height: 64
+Colors: 2
+Controller: ks0108
+Controllers: 2
+Pages: 8 each controller
+Addresses: 64 each page
+
+
+If you compiled the device as a module, don't remember to
+update udev to get the new device node at /dev.
+
+
+---------
+2. WIRING
+---------
+
+The cfag12864b LCD Display Series don't have a official wiring.
+
+The common wiring is done to the parallel port:
+
+http://www.skippari.net/lcd/sekalaista/crystalfontz_cfag12864B-TMI-V.png
+
+You can get help at Crystalfontz and LCDInfo forums.
+
+
+
+-------------------------
+3. USER-SPACE PROGRAMMING
+-------------------------
+
+Include a copy of the provided header:
+
+ #include "cfag12864b.h"
+
+Open the device for writing, /dev/cfag12864b0:
+
+ int fd = open("/dev/cfag12864b0",O_WRONLY);
+
+Then use simple ioctl calls to control it:
+
+ ioctl(fdisplay,CFAG12864B_IOCOFF); /* Turn off (don't clear) */
+ ioctl(fdisplay,CFAG12864B_IOCON); /* Turn on */
+ ioctl(fdisplay,CFAG12864B_IOCCLEAR); /* Clear the display */
+
+For writing to the display, you have two options:
+
+
+3.1. ioctl & 128*64 boolean matrix
+-------------------------------------------------------
+
+This method is easier, but you have to update the entire display
+each time you want to change it.
+
+Note:
+
+ CFAG12864B_FORMATSIZE ==
+ CFAG12864B_WIDTH * CFAG12864B_HEIGHT ==
+ 128 * 64
+
+Declare the matrix and other one:
+
+ unsigned char MyDrawing[CFAG12864B_WIDTH][CFAG12864B_HEIGHT];
+
+ unsigned char Buffer[CFAG12864B_FORMATSIZE];
+
+Copy the 2d matrix to the buffer , like:
+
+ for(i=0;i<CFAG12864B_WIDTH;++i)
+ for(j=0;j<CFAG12864B_HEIGHT;++j)
+ Buffer[i+j*CFAG12864B_WIDTH]=MyDrawing[i][j];
+
+Call the ioctl:
+
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer);
+
+Voila! Your drawing should appear on the screen.
+
+
+
+3.2. Direct writing
+-------------------
+
+This methods allows you to change each byte of the device,
+so you can achieve a higher update rate.
+
+The device size is 1024 == CFAG12864B_SIZE.
+
+You can write and seek the device. The first 512 bytes write to
+the first k0108 controller (left display half) and the last 512 bytes
+write to the second ks0108 controller (right display half).
+
+Each controller is divided into 8 pages. Each page has 64 bytes.
+
+ Controller 0 Controller 1
+ _________________________
+Page 0 |____________|____________|
+Page 1 |____________|____________|
+Page 2 |____________|____________|
+Page 3 |____________|____________|
+Page 4 |____________|____________|
+Page 5 |____________|____________|
+Page 6 |____________|____________|
+Page 7 |____________|____________|
+ <--- 64 --->
+
+You will understand how the device work executing some commands:
+
+ # echo -n A > /dev/cfag12864b0
+ # echo -n a > /dev/cfag12864b0
+ # echo AAAAAA > /dev/cfag12864b0
+ # echo 000000 > /dev/cfag12864b0
+ # echo Hello world! > /dev/cfag12864b0
+ # echo Hello world! Hello world! > /dev/cfag12864b0
+
+After you understand it, code your functions to change specific bytes.
+
+Use write() and lseek() system calls, like:
+
+ lseek(fdisplay,ipage*CFAG12864B_HEIGHT,SEEK_SET);
+ lseek(fdisplay,icontroller*CFAG12864B_SIZE/2,SEEK_SET);
+
+ write(fdisplay,bufpage,CFAG12864B_HEIGHT);
+ write(fdisplay,bufcontroller,CFAG12864B_SIZE/2);
+ write(fdisplay,bufdisplay,CFAG12864B_SIZE);
+
+
+---------------
+4. USEFUL FILES
+---------------
+
+
+4.1 cfag12864b.h
+----------------
+
+You can use a copy of this header in your user-space programs.
+
+---
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header for user-space apps
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <sys/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_FORMATSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+#define CFAG12864B_SIZE 1024
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+#endif // _CFAG12864B_H_
+---
+
+
+
+4.2 Example BMP writer
+----------------------
+
+You can take ideas from this code. It reads a .bmp file,
+convert it to a boolean [128*64] buffer and then use
+ioctl to display it on the screen.
+
+---
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "cfag12864b.h"
+
+#define BMP_SIZE 1024
+
+union dword
+{
+ unsigned int u32;
+ unsigned char u8[4];
+};
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+
+void BMP2Format(
+ unsigned char _Src[BMP_SIZE],
+ unsigned char _Dest[CFAG12864B_FORMATSIZE])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Bits = 8;
+
+ unsigned int Y,X,Bit;
+
+ for(Y=0; Y<Height; ++Y)
+ for(X=0; X<Width/Bits; ++X)
+ for(Bit=0; Bit<Bits; ++Bit)
+ _Dest[X*Bits+Bit+(Height-Y-1)*Width] =
+ _Src[Y*Width/Bits+X]&Bit(Bits-Bit-1)?0:1;
+}
+
+int main(int argc, char * argv[])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Size = CFAG12864B_SIZE;
+ const unsigned int BPP = 1;
+ const unsigned int HeaderSize = 0x3E;
+ const unsigned int BMPSize = BMP_SIZE;
+
+ unsigned char c;
+ unsigned int i,j;
+ union dword n;
+
+ unsigned char Buffer_BMP[BMP_SIZE];
+ unsigned char Buffer_Matrix[CFAG12864B_FORMATSIZE];
+
+ int fdisplay;
+ FILE * fbmp;
+
+ // Check args
+ if(argc!=3) {
+ printf("%s: Bad number of arguments. Expected 3\n",
+ argv[0]);
+ return -1;
+ }
+
+ // Open file
+ fbmp = fopen(argv[2],"rb");
+ if(fbmp==NULL) {
+ printf("%s: Can't open %s\n",argv[0], argv[2]);
+ return -2;
+ }
+
+ // Check file size
+ fseek(fbmp,0,SEEK_END);
+ i=ftell(fbmp);
+ if(i!=HeaderSize+Size) {
+ printf("%s: Bad file size. %i instead of %i\n",
+ argv[0], i, HeaderSize+Size);
+ fclose(fbmp);
+ return -3;
+ }
+
+ // Check both magic BMP bytes
+ fseek(fbmp,0,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!='B') {
+ printf("%s: Bad first magic byte. '%c' instead of 'B'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -4;
+ }
+ c = fgetc(fbmp);
+ if(c!='M') {
+ printf("%s: Bad second magic byte. '%c' instead of 'M'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -5;
+ }
+
+ // Check this is a 128x64 1-bpp BMP file
+ fseek(fbmp,0x12,SEEK_SET);
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Width) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Width);
+ fclose(fbmp);
+ return -6;
+ }
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Height) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Height);
+ fclose(fbmp);
+ return -7;
+ }
+ fseek(fbmp,0x1C,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!=BPP) {
+ printf("%s: Bad bpp. %i instead of %i\n",
+ argv[0], c, BPP);
+ fclose(fbmp);
+ return -8;
+ }
+
+ // Get bitmap data
+ fseek(fbmp,0x3E,SEEK_SET);
+ for(i=0; i<BMPSize; ++i)
+ Buffer_BMP[i]=fgetc(fbmp);
+ fclose(fbmp);
+
+ // Transform BMP data to 2D matrix
+ BMP2Format(Buffer_BMP,Buffer_Matrix);
+
+ // Open file
+ fdisplay = open(argv[1],O_WRONLY);
+ if(fdisplay < 0) {
+ printf("%s: Can't open %s\n", argv[0], argv[1]);
+ return -9;
+ }
+
+ // Send matrix
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer_Matrix);
+
+ // Close file
+ close(fdisplay);
+
+ return 0;
+}
+---
+
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/display
linux-2.6.17.13/Documentation/drivers/display/display
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/display
1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/display 2006-09-12
00:24:39.000000000 +0200
@@ -0,0 +1,37 @@
+ =============================
+ Display Drivers Documentation
+ =============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+--------
+0. INDEX
+--------
+
+ 1. NEW DISPLAY DRIVERS
+ 2. GENERAL TIPS
+
+
+
+----------------------
+1. NEW DISPLAY DRIVERS
+----------------------
+
+Feel free to send me new display drivers. I will try to do my best.
+
+If you don't get any answer, send your patch directly to the linux-kernel ml.
+
+
+
+---------------
+2. GENERAL TIPS
+---------------
+
+- Divide your driver into the controller driver, like ks0108,
+ and the specific LCD display series driver, like cfag12864b.
+
+- Claim for your IO ports in the controller driver.
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt
linux-2.6.17.13/Documentation/ioctl-number.txt
--- linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt 2006-09-09
05:23:25.000000000 +0200
+++ linux-2.6.17.13/Documentation/ioctl-number.txt 2006-09-12
00:36:48.000000000 +0200
@@ -190,3 +190,5 @@ Code Seq# Include File Comments
<mailto:[email protected]>
0xF3 00-3F video/sisfb.h sisfb (in development)
<mailto:[email protected]>
+0xFF 00-1F cfag12864b LCD Display linux/cfag12864b.h
+ <mailto:[email protected]>
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/CREDITS linux-2.6.17.13/CREDITS
--- linux-2.6.17.13-vanilla/CREDITS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/CREDITS 2006-09-12 00:16:06.000000000 +0200
@@ -2534,6 +2534,14 @@ S: Subiaco, 6008
S: Perth, Western Australia
S: Australia

+N: Miguel Ojeda Sandonis
+E: [email protected]
+D: Author: LCD Display Drivers (ks0108, cfag12864b)
+D: Maintainer: LCD Display Drivers Tree (drivers/display/*)
+S: C/ Mieses 20, 9-B
+S: Valladolid 47009
+S: Spain
+
N: Greg Page
E: [email protected]
D: IPX development and support

2006-09-11 23:57:59

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH 2/2] display: Driver ks0108 and cfag12864b

Miguel Ojeda Sandonis

Adds support for additional "display" devices, like small LCD screens.
Adds support for the ks0108 LCD Controller.
Adds support for the cfag12864b LCD.

Signed-off-by: Miguel Ojeda Sandonis <[email protected]>
---
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c
linux-2.6.17.13/drivers/display/cfag12864b.c
--- linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/cfag12864b.c 2006-09-11
23:48:59.000000000 +0200
@@ -0,0 +1,585 @@
+/*
+ * Filename: cfag12864b.c
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver
+ * License: GPL
+ * Depends: display ks0108
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/display.h>
+#include <linux/ks0108.h>
+#include <linux/cfag12864b.h>
+#include <asm/uaccess.h>
+
+#define NAME "cfag12864b"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Device
+//
+
+static const unsigned int FirstMinor = 0;
+static const unsigned int nDevices = 1;
+static const char * Name = NAME;
+
+static int Major;
+static dev_t FirstDevice;
+
+struct cfag12864b
+{
+ int Minor;
+ dev_t Device;
+ struct cdev CharDevice;
+};
+
+static struct cfag12864b * Devices;
+
+
+
+//
+// cfag12864b Commands
+//
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+#define NoBit(n) ((unsigned char)(~Bit(n)))
+static const unsigned int Bits = 8;
+
+static const unsigned int Width = CFAG12864B_WIDTH;
+static const unsigned int Height = CFAG12864B_HEIGHT;
+static const unsigned int MatrixSize = CFAG12864B_MATRIXSIZE;
+static const unsigned int Controllers = CFAG12864B_CONTROLLERS;
+static const unsigned int Pages = CFAG12864B_PAGES;
+static const unsigned int Addresses = CFAG12864B_ADDRESSES;
+static const unsigned int Size = CFAG12864B_SIZE;
+
+static unsigned char State = 0;
+
+void cfag12864b_Set(void)
+{
+ ks0108_WriteControl(State ^ (Bit(3) | Bit(1) | Bit(0)) );
+}
+
+void cfag12864b_E(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(0);
+ else
+ State &= NoBit(0);
+ cfag12864b_Set();
+}
+
+void cfag12864b_CS1(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(2);
+ else
+ State &= NoBit(2);
+ cfag12864b_Set();
+}
+
+void cfag12864b_CS2(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(1);
+ else
+ State &= NoBit(1);
+ cfag12864b_Set();
+}
+
+void cfag12864b_DI(unsigned char _State)
+{
+ if(_State)
+ State |= Bit(3);
+ else
+ State &= NoBit(3);
+ cfag12864b_Set();
+}
+
+void cfag12864b_FirstController(unsigned char _State)
+{
+ if(_State)
+ cfag12864b_CS1(0);
+ else
+ cfag12864b_CS1(1);
+}
+
+void cfag12864b_SecondController(unsigned char _State)
+{
+ if(_State)
+ cfag12864b_CS2(0);
+ else
+ cfag12864b_CS2(1);
+}
+
+void cfag12864b_Controllers(unsigned char _First, unsigned char _Second)
+{
+ cfag12864b_FirstController(_First);
+ cfag12864b_SecondController(_Second);
+}
+
+void cfag12864b_Controller(unsigned char _Which)
+{
+ if(_Which==0)
+ cfag12864b_Controllers(1,0);
+ else if(_Which==1)
+ cfag12864b_Controllers(0,1);
+}
+
+void cfag12864b_DisplayState(unsigned char _State)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_DisplayState(_State);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_Address(unsigned char _Address)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_Address(_Address);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_Page(unsigned char _Page)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_Page(_Page);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_StartLine(unsigned char _StartLine)
+{
+ cfag12864b_DI(0);
+ cfag12864b_E(1);
+ ks0108_StartLine(_StartLine);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_WriteByte(unsigned char _Byte)
+{
+ cfag12864b_DI(1);
+ cfag12864b_E(1);
+ ks0108_WriteData(_Byte);
+ cfag12864b_E(0);
+}
+
+void cfag12864b_NOP(void)
+{
+ cfag12864b_StartLine(0);
+}
+
+
+
+//
+// Auxiliar
+//
+
+void NormalizeOffset(unsigned int * _Offset)
+{
+ if(*_Offset>=Pages*Addresses)
+ *_Offset-=Pages*Addresses;
+}
+
+unsigned char GetAddress(unsigned int _Offset)
+{
+ NormalizeOffset(&_Offset);
+ return _Offset%Addresses;
+}
+
+unsigned char GetController(unsigned int _Offset)
+{
+ if(_Offset<Pages*Addresses)
+ return 0;
+ return 1;
+}
+
+unsigned char GetPage(unsigned int _Offset)
+{
+ NormalizeOffset(&_Offset);
+ return _Offset/Addresses;
+}
+
+
+
+//
+// cfag12864b Exported Commands
+//
+
+void cfag12864b_On(void)
+{
+ cfag12864b_Controllers(1,1);
+ cfag12864b_DisplayState(1);
+}
+
+void cfag12864b_Off(void)
+{
+ cfag12864b_Controllers(1,1);
+ cfag12864b_DisplayState(0);
+}
+
+void cfag12864b_Clear(void)
+{
+ unsigned char Page,Address;
+
+ cfag12864b_Controllers(1,1);
+ for(Page=0; Page<Pages; ++Page) {
+ cfag12864b_Page(Page);
+ cfag12864b_Address(0);
+ for(Address=0; Address<Addresses; ++Address)
+ cfag12864b_WriteByte(0x00);
+ }
+}
+
+void cfag12864b_Write(
+ unsigned short _Offset,
+ unsigned char * _Buffer,
+ unsigned short _Count)
+{
+ unsigned short i;
+
+ // Invalid values: They get updated at the first cycle
+ unsigned char Controller = 0xFF;
+ unsigned char Page = 0xFF;
+ unsigned char Address = 0xFF;
+
+ unsigned char tmpController, tmpPage, tmpAddress;
+
+ for(i=0; i<_Count; ++i, ++_Offset, ++Address) {
+ tmpController = GetController(_Offset);
+ tmpPage = GetPage(_Offset);
+ tmpAddress = GetAddress(_Offset);
+
+ if(Controller != tmpController) {
+ Controller = tmpController;
+ cfag12864b_Controller(Controller);
+ cfag12864b_NOP();
+ }
+ if(Page != tmpPage) {
+ Page = tmpPage;
+ cfag12864b_Page(Page);
+ cfag12864b_NOP();
+ }
+
+ /*if(Address != tmpAddress) {
+ Address = tmpAddress;
+ cfag12864b_Address(Address);
+ cfag12864b_NOP();
+ }*/
+
+ /*if(tmpController == 0) {
+ if(Address != tmpAddress) {
+ Address = tmpAddress;
+ cfag12864b_Address(Address);
+ }
+ }
+ else {
+ cfag12864b_Address(tmpAddress);
+ cfag12864b_NOP();
+ }*/
+
+ // Safe method, still quick
+ cfag12864b_Address(tmpAddress);
+ cfag12864b_NOP();
+
+ // Dummy
+ cfag12864b_NOP();
+
+ cfag12864b_WriteByte(_Buffer[i]);
+ }
+}
+
+void cfag12864b_Format(unsigned char * _Src)
+{
+ unsigned short Controller,Page,Address,Bit;
+ unsigned char * Dest = kmalloc(sizeof(unsigned char)*Size,GFP_KERNEL);
+
+ if(Dest == NULL) {
+ printk(PRINTK_PREFIX "Format: ERROR: "
+ "kmalloc\n");
+ return;
+ }
+
+ for(Controller=0; Controller<Controllers; ++Controller)
+ for(Page=0; Page<Pages; ++Page)
+ for(Address=0; Address<Addresses; ++Address) {
+ Dest[(Controller*Pages+Page)*Addresses+Address]=0;
+ for(Bit=0; Bit<Bits; ++Bit)
+
if(_Src[Controller*Addresses+Address+(Page*Bits+Bit)*Width])
+
Dest[(Controller*Pages+Page)*Addresses+Address]|=Bit(Bit);
+ }
+
+ cfag12864b_Write(0,Dest,Size);
+
+ kfree(Dest);
+}
+
+EXPORT_SYMBOL_GPL(cfag12864b_On);
+EXPORT_SYMBOL_GPL(cfag12864b_Off);
+EXPORT_SYMBOL_GPL(cfag12864b_Clear);
+EXPORT_SYMBOL_GPL(cfag12864b_Write);
+EXPORT_SYMBOL_GPL(cfag12864b_Format);
+
+
+//
+// Fops
+//
+
+int Open(
+ struct inode * _Inode,
+ struct file * _File)
+{
+ _File->private_data = container_of(_Inode->i_cdev, struct
cfag12864b, CharDevice);
+
+ return 0;
+}
+
+loff_t Seek(
+ struct file * _File,
+ loff_t _Offset,
+ int _Whence)
+{
+ loff_t New;
+
+ switch(_Whence) {
+ case 0: // SEEK_SET
+ New = _Offset;
+ break;
+ case 1: // SEEK_CUR
+ New = _File->f_pos + _Offset;
+ break;
+ case 2: // SEEK_END
+ New = Size + _Offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if(New < 0)
+ return -EINVAL;
+ _File->f_pos = New;
+ return New;
+}
+
+
+ssize_t Write(
+ struct file * _File,
+ const char __user * _Buffer,
+ size_t _Count,
+ loff_t * _Offset)
+{
+ int Result;
+ char * Buffer;
+
+ if(*_Offset>Size)
+ return 0;
+ if(*_Offset+_Count>Size)
+ _Count=Size-*_Offset;
+
+ Buffer = kmalloc(_Count,GFP_KERNEL);
+ if(Buffer == NULL) {
+ printk(PRINTK_PREFIX "FOP Write: ERROR: "
+ "kmalloc\n");
+ return -ENOMEM;
+ }
+
+ Result = copy_from_user(Buffer, _Buffer, _Count);
+ if(Result != 0) {
+ printk(PRINTK_PREFIX "FOP Write: ERROR: "
+ "copy_from_user\n");
+ Result = -EFAULT;
+ goto out;
+ }
+
+ cfag12864b_Write(*_Offset, Buffer, _Count);
+
+ *_Offset += _Count;
+ Result = _Count;
+
+out:
+ kfree(Buffer);
+ return Result;
+}
+
+int IOCtl_Format(unsigned long _Arg)
+{
+ int Result,Return = -ENOTTY;
+
+ unsigned char * IOCFormatSrc;
+
+ // Alloc src buffer
+ IOCFormatSrc = kmalloc(sizeof(unsigned char)*Width*Height,GFP_KERNEL);
+ if(IOCFormatSrc == NULL) {
+ printk(PRINTK_PREFIX "FOP IOCtl: ERROR: "
+ "IOC FormatWrite: "
+ "kmalloc\n");
+ goto out;
+ }
+
+ // Copy src buffer from user
+ Result = copy_from_user(
+ IOCFormatSrc,
+ (void __user *)_Arg,
+ sizeof(unsigned char)*Width*Height);
+ if(Result != 0) {
+ printk(PRINTK_PREFIX "FOP IOCtl: ERROR: "
+ "IOC FormatWrite: "
+ "copy_from_user(IOCFormatSrc,...\n");
+ goto allocsrc;
+ }
+
+ cfag12864b_Format(IOCFormatSrc);
+
+ Return = 0;
+
+allocsrc:
+ kfree(IOCFormatSrc);
+out:
+ return Return;
+}
+
+int IOCtl(
+ struct inode * _Inode,
+ struct file * _File,
+ unsigned int _Cmd,
+ unsigned long _Arg)
+{
+ if(_IOC_TYPE(_Cmd) != CFAG12864B_IOC_MAGIC)
+ return -ENOTTY;
+ if(_IOC_NR(_Cmd) > CFAG12864B_IOC_MAXNR)
+ return -ENOTTY;
+
+ switch(_Cmd) {
+ case CFAG12864B_IOCON:
+ cfag12864b_On();
+ break;
+ case CFAG12864B_IOCOFF:
+ cfag12864b_Off();
+ break;
+ case CFAG12864B_IOCCLEAR:
+ cfag12864b_Clear();
+ break;
+ case CFAG12864B_IOCFORMAT:
+ return IOCtl_Format(_Arg);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+
+
+struct file_operations Fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = Seek,
+ .write = Write,
+ .ioctl = IOCtl,
+ .open = Open,
+};
+
+
+
+
+
+
+
+
+
+//
+// Module Init & Exit
+//
+
+int cfag12864b_init(void)
+{
+ unsigned int i;
+ int Result;
+
+ printk(PRINTK_PREFIX "Init... ");
+
+ Result = alloc_chrdev_region(&FirstDevice, FirstMinor, nDevices, Name);
+ if(Result < 0) {
+ printk("ERROR - alloc_chrdev_region\n");
+ return Result;
+ }
+ Major = MAJOR(FirstDevice);
+
+ Devices = kmalloc(nDevices * sizeof(struct cfag12864b), GFP_KERNEL);
+ if(Devices == NULL) {
+ printk("ERROR - kmalloc\n");
+ return -ENOMEM;
+ }
+ memset(Devices, 0, nDevices * sizeof(struct cfag12864b));
+
+ for(i=0; i<nDevices; ++i) {
+ Devices[i].Minor = FirstMinor+i;
+ Devices[i].Device = MKDEV(Major,Devices[i].Minor);
+
+ cdev_init(&(Devices[i].CharDevice), &Fops);
+ Devices[i].CharDevice.owner = THIS_MODULE;
+ Devices[i].CharDevice.ops = &Fops;
+ Result = cdev_add(&(Devices[i].CharDevice),
+ Devices[i].Device, 1);
+ if(Result < 0) {
+ printk("ERROR - cdev_add\n");
+ kfree(Devices);
+ return Result;
+ }
+
+ class_device_create(
+ display_class,NULL,MKDEV(Major,Devices[i].Minor),
+ NULL,"cfag12864b%d",Devices[i].Minor);
+ }
+
+
+ cfag12864b_Clear();
+ cfag12864b_On();
+
+ printk("Done\n");
+
+ return 0;
+}
+
+void cfag12864b_exit(void)
+{
+ unsigned int i;
+
+ printk(PRINTK_PREFIX "Exit... ");
+
+ cfag12864b_Off();
+
+ if(Devices != NULL) {
+ for(i=0; i<nDevices; ++i) {
+
class_device_destroy(display_class,MKDEV(Major,Devices[i].Minor));
+ cdev_del(&(Devices[i].CharDevice));
+ }
+ kfree(Devices);
+ }
+
+ unregister_chrdev_region(FirstDevice, nDevices);
+
+ printk("Done\n");
+}
+
+module_init(cfag12864b_init);
+module_exit(cfag12864b_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("cfag12864b");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/display.c
linux-2.6.17.13/drivers/display/display.c
--- linux-2.6.17.13-vanilla/drivers/display/display.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/display.c 2006-09-11
23:53:22.000000000 +0200
@@ -0,0 +1,60 @@
+/*
+ * Filename: display.c
+ * Version: 0.1.0
+ * Description: Display Class
+ * License: GPL
+ * Depends: -
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/display.h>
+
+#define NAME "display"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+static char * Name = NAME;
+
+//
+// Exported Display Data
+//
+
+struct class * display_class;
+EXPORT_SYMBOL_GPL(display_class);
+
+
+//
+// Module Init & Exit
+//
+
+int display_init(void)
+{
+ display_class = class_create(THIS_MODULE,Name);
+ if(IS_ERR(display_class)) {
+ printk(PRINTK_PREFIX "ERROR: class_simple_create\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void display_exit(void)
+{
+ class_destroy(display_class);
+}
+
+module_init(display_init);
+module_exit(display_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("display");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Kconfig
linux-2.6.17.13/drivers/display/Kconfig
--- linux-2.6.17.13-vanilla/drivers/display/Kconfig 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Kconfig 2006-09-12
00:04:14.000000000 +0200
@@ -0,0 +1,61 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+# Display drivers configuration.
+#
+
+menu "Display support"
+
+config DISPLAY
+ tristate "Display support"
+ default n
+ ---help---
+ If you have an extra display, like a small LCD screen, say Y.
+
+ To compile this as a module, choose M here:
+ module will be called display.
+
+ If unsure, say N.
+
+comment "Parallel port dependent:"
+
+config KS0108
+ tristate "KS0108 LCD Controller"
+ depends on DISPLAY && PARPORT
+ default n
+ ---help---
+ If you have a LCD display controlled by one or more KS0108
+ controllers, say Y. You will need also another more specific
+ driver for your LCD.
+
+ Depends on Parallel Port support. If you say Y at
+ parport, you will be able to compile this as a module (M)
+ and built-in as well (Y). If you said M at parport,
+ you will be able only to compile this as a module (M).
+
+ To compile this as a module, choose M here:
+ module will be called ks0108.
+
+ If unsure, say N.
+
+config CFAG12864B
+ tristate "CFAG12864B LCD Display"
+ depends on KS0108
+ default n
+ ---help---
+ If you have a Crystalfontz 128x64 2-color LCD display,
+ cfag12864b Series, say Y. You also need the ks0108 LCD
+ Controller driver.
+
+ For help about how to wire your LCD to the parallel port,
+ check this image: http://www.skippari.net/lcd/sekalaista
+ /crystalfontz_cfag12864B-TMI-V.png
+
+ To compile this as a module, choose M here:
+ module will be called cfag12864b.
+
+ If unsure, say N.
+
+endmenu
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/ks0108.c
linux-2.6.17.13/drivers/display/ks0108.c
--- linux-2.6.17.13-vanilla/drivers/display/ks0108.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/ks0108.c 2006-09-11
23:49:15.000000000 +0200
@@ -0,0 +1,177 @@
+/*
+ * Filename: ks0108.c
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver
+ * License: GPL
+ * Depends: parport
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/parport.h>
+#include <linux/ks0108.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+#define NAME "ks0108"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Module
+//
+
+static unsigned int Port = 0x378;
+module_param(Port,uint,S_IRUGO);
+
+
+
+//
+// Device
+//
+
+static const char * Name = NAME;
+
+static struct parport * Parport;
+static struct pardevice * Pardevice;
+
+
+
+//
+// ks0108 Exported Commands
+//
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+
+void ks0108_WriteData(unsigned char _Byte)
+{
+ parport_write_data(Parport,_Byte);
+}
+
+void ks0108_WriteControl(unsigned char _Byte)
+{
+ const unsigned int ECycleDelay = 1;
+ udelay(ECycleDelay);
+ parport_write_control(Parport,_Byte);
+}
+
+void ks0108_DisplayState(unsigned char _State)
+{
+ unsigned char Command = Bit(1) | Bit(2) | Bit(3) | Bit(4) | Bit(5);
+ if(_State)
+ Command |= Bit(0);
+ ks0108_WriteData(Command);
+}
+
+void ks0108_StartLine(unsigned char _StartLine)
+{
+ const unsigned char MaxStartLine = 63;
+ unsigned char Command = Bit(6) | Bit(7);
+ if(_StartLine>MaxStartLine)
+ _StartLine=MaxStartLine;
+ Command |= _StartLine;
+ ks0108_WriteData(Command);
+}
+
+void ks0108_Address(unsigned char _Address)
+{
+ const unsigned char MaxAddress = 63;
+ unsigned char Command = Bit(6);
+ if(_Address>MaxAddress)
+ _Address=MaxAddress;
+ Command |= _Address;
+ ks0108_WriteData(Command);
+}
+
+void ks0108_Page(unsigned char _Page)
+{
+ const unsigned char MaxPage = 7;
+ unsigned char Command = Bit(3) | Bit(4) | Bit(5) | Bit(7);
+ if(_Page>MaxPage)
+ _Page=MaxPage;
+ Command |= _Page;
+ ks0108_WriteData(Command);
+}
+
+
+EXPORT_SYMBOL_GPL(ks0108_WriteData);
+EXPORT_SYMBOL_GPL(ks0108_WriteControl);
+EXPORT_SYMBOL_GPL(ks0108_DisplayState);
+EXPORT_SYMBOL_GPL(ks0108_StartLine);
+EXPORT_SYMBOL_GPL(ks0108_Address);
+EXPORT_SYMBOL_GPL(ks0108_Page);
+
+
+
+
+//
+// Module Init & Exit
+//
+
+int ks0108_init(void)
+{
+ int Return = -EINVAL;
+
+ printk(PRINTK_PREFIX "Init... ");
+
+ Parport = parport_find_base(Port);
+ if(Parport == NULL) {
+ printk("ERROR - parport_find_base\n");
+ Return = -EFAULT;
+ goto none;
+ }
+
+ Pardevice = parport_register_device(
+ Parport,Name,
+ NULL,NULL,NULL,
+ PARPORT_DEV_EXCL,NULL);
+ if(Pardevice == NULL) {
+ printk("ERROR: parport_register_device\n");
+ Return = -EFAULT;
+ goto none;
+ }
+
+ if(parport_claim(Pardevice) != 0) {
+ printk("ERROR: parport_claim\n");
+ Return = -EFAULT;
+ goto registered;
+ }
+
+ printk("Done\n");
+ return 0;
+
+registered:
+ parport_unregister_device(Pardevice);
+
+none:
+ return Return;
+}
+
+void ks0108_exit(void)
+{
+ printk(PRINTK_PREFIX "Exit... ");
+
+ parport_release(Pardevice);
+
+ parport_unregister_device(Pardevice);
+
+ printk("Done\n");
+}
+
+module_init(ks0108_init);
+module_exit(ks0108_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("ks0108");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Makefile
linux-2.6.17.13/drivers/display/Makefile
--- linux-2.6.17.13-vanilla/drivers/display/Makefile 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Makefile 2006-09-11
18:00:05.000000000 +0200
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel Display device drivers.
+#
+
+obj-$(CONFIG_DISPLAY) += display.o
+obj-$(CONFIG_KS0108) += ks0108.o
+obj-$(CONFIG_CFAG12864B) += cfag12864b.o
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Kconfig
linux-2.6.17.13/drivers/Kconfig
--- linux-2.6.17.13-vanilla/drivers/Kconfig 2006-09-09
05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Kconfig 2006-09-11 19:04:53.000000000 +0200
@@ -72,4 +72,8 @@ source "drivers/edac/Kconfig"

source "drivers/rtc/Kconfig"

+# parport before display - some displays depend on it.
+
+source "drivers/display/Kconfig"
+
endmenu
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Makefile
linux-2.6.17.13/drivers/Makefile
--- linux-2.6.17.13-vanilla/drivers/Makefile 2006-09-09
05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Makefile 2006-09-11 19:15:50.000000000 +0200
@@ -74,3 +74,4 @@ obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
+obj-$(CONFIG_DISPLAY) += display/
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/cfag12864b.h
linux-2.6.17.13/include/linux/cfag12864b.h
--- linux-2.6.17.13-vanilla/include/linux/cfag12864b.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/cfag12864b.h 2006-09-11
17:32:19.000000000 +0200
@@ -0,0 +1,45 @@
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <linux/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_MATRIXSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+
+#define CFAG12864B_CONTROLLERS 2
+#define CFAG12864B_PAGES 8
+#define CFAG12864B_ADDRESSES 64
+#define CFAG12864B_SIZE CFAG12864B_CONTROLLERS * \
+ CFAG12864B_PAGES * \
+ CFAG12864B_ADDRESSES
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+#define CFAG12864B_IOC_MAXNR 0x03
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+extern void cfag12864b_On(void);
+extern void cfag12864b_Off(void);
+extern void cfag12864b_Clear(void);
+extern void cfag12864b_Write(
+ unsigned short Offset,
+ unsigned char * Buffer,
+ unsigned short Count);
+extern void cfag12864b_Format(unsigned char * Src);
+
+#endif // _CFAG12864B_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/display.h
linux-2.6.17.13/include/linux/display.h
--- linux-2.6.17.13-vanilla/include/linux/display.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/display.h 2006-09-11
19:25:44.000000000 +0200
@@ -0,0 +1,19 @@
+/*
+ * Filename: display.h
+ * Version: 0.1.0
+ * Description: Display Class Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include <linux/device.h>
+
+extern struct class * display_class;
+
+#endif // _DISPLAY_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/ks0108.h
linux-2.6.17.13/include/linux/ks0108.h
--- linux-2.6.17.13-vanilla/include/linux/ks0108.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/ks0108.h 2006-09-11
23:27:26.000000000 +0200
@@ -0,0 +1,22 @@
+/*
+ * Filename: ks0108.h
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _KS0108_H_
+#define _KS0108_H_
+
+extern void ks0108_WriteData(unsigned char Byte);
+extern void ks0108_WriteControl(unsigned char Byte);
+extern void ks0108_DisplayState(unsigned char State);
+extern void ks0108_StartLine(unsigned char StartLine);
+extern void ks0108_Address(unsigned char Address);
+extern void ks0108_Page(unsigned char Page);
+
+#endif // _KS0108_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/MAINTAINERS linux-2.6.17.13/MAINTAINERS
--- linux-2.6.17.13-vanilla/MAINTAINERS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/MAINTAINERS 2006-09-12 00:17:51.000000000 +0200
@@ -1640,6 +1640,11 @@ M: [email protected]
L: [email protected]
S: Maintained

+DISPLAY DRIVERS
+P: Miguel Ojeda Sandonis
+M: [email protected]
+S: Maintained
+
LED SUBSYSTEM
P: Richard Purdie
M: [email protected]

2006-09-12 01:13:44

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH 2/2] display: Driver ks0108 and cfag12864b

On Tue, Sep 12, 2006 at 01:57:23AM +0200, Miguel Ojeda wrote:
> +int cfag12864b_init(void)

arrrrggg.... I'm in the middle of reading every module_init and every
module_exit func, and this starts getting really annoying....

1. module_init function SHOULD be written as

static int __init FOO_init_module(void)
{
...
}

2. module_exit function SHOULD be written as

static void __exit FOO_exit_module(void)
{
..
}

3. If one of function during module initialization returns an
error, everything done so far MUST be backed out to not cause
leaks.

4. module_init and module_exit functions SHOULD NOT spit useless
messages upon which system administrator can't act meaningfully.

5. In case of error during initialization, error code SHOULD be
propagated as is to upper layers, either via direct
assignment/return or via decoding from pointer.

6. Error messages SHOULD start with short unique prefix specific to
driver. Module name without .o and .ko is fine.

7. StupidCapitalization MUST NOT be used.

8. Function args SHOULD NOT be prefixed with underscore without
reason.

9. Various sorts of operations and methods driver provides to
upper layers SHOULD start with short, common to driver
prefix and SHOULD be made static where possible:

static struct foobar_operations rtl8139_fops = {
...
};

> +{
> + unsigned int i;
> + int Result;
> +
> + printk(PRINTK_PREFIX "Init... ");
> +
> + Result = alloc_chrdev_region(&FirstDevice, FirstMinor, nDevices,
> Name);
> + if(Result < 0) {
> + printk("ERROR - alloc_chrdev_region\n");
> + return Result;
> + }
> + Major = MAJOR(FirstDevice);
> +
> + Devices = kmalloc(nDevices * sizeof(struct cfag12864b), GFP_KERNEL);
> + if(Devices == NULL) {
> + printk("ERROR - kmalloc\n");
> + return -ENOMEM;
> + }
> + memset(Devices, 0, nDevices * sizeof(struct cfag12864b));
> +
> + for(i=0; i<nDevices; ++i) {
> + Devices[i].Minor = FirstMinor+i;
> + Devices[i].Device = MKDEV(Major,Devices[i].Minor);
> +
> + cdev_init(&(Devices[i].CharDevice), &Fops);
> + Devices[i].CharDevice.owner = THIS_MODULE;
> + Devices[i].CharDevice.ops = &Fops;
> + Result = cdev_add(&(Devices[i].CharDevice),
> + Devices[i].Device, 1);
> + if(Result < 0) {
> + printk("ERROR - cdev_add\n");
> + kfree(Devices);
> + return Result;
> + }
> +
> + class_device_create(
> + display_class,NULL,MKDEV(Major,Devices[i].Minor),
> + NULL,"cfag12864b%d",Devices[i].Minor);
> + }
> +
> +
> + cfag12864b_Clear();
> + cfag12864b_On();

->open, ->write could be called as soon as cdev_add succeeds.
Is hardware functional at that point?

2006-09-12 02:37:55

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH 2/2] display: Driver ks0108 and cfag12864b

On 9/12/06, Alexey Dobriyan <[email protected]> wrote:
>
> arrrrggg.... I'm in the middle of reading every module_init and every
> module_exit func, and this starts getting really annoying....
>

Anyway, thank you for you time reviewing it. :)


> 5. In case of error during initialization, error code SHOULD be
> propagated as is to upper layers, either via direct
> assignment/return or via decoding from pointer.
>

What do you mean? I thought returning the error code was enough.


> 6. Error messages SHOULD start with short unique prefix specific to
> driver. Module name without .o and .ko is fine.

They do 8)

#define PRINTK_PREFIX PRINTK_INFO NAME ": "

printk(PRINTK_PREFIX "Init... ");

Then, if a error appears: printk("ERROR - kmalloc\n");

Final string: "<i>cfag12864b: Init... ERROR - kmalloc"

The only bad point: If some other printk is called from other module
between. Should I change it?


Miguel Ojeda

2006-09-12 03:57:19

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 2.6.17.13] display: Driver ks0108 and cfag12864b

On Tue, Sep 12, 2006 at 01:27:30AM +0200, Miguel Ojeda wrote:
> Miguel Ojeda Sandonis
>
> Adds support for additional "display" devices, like small LCD screens.
> Adds support for the ks0108 LCD Controller.
> Adds support for the cfag12864b LCD.
>
> Signed-off-by: Miguel Ojeda Sandonis <[email protected]>
> ---
> diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
> linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
> linux-2.6.17.13/Documentation/drivers/display/cfag12864b
> --- linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
> 1970-01-01
> 01:00:00.000000000 +0100
> +++ linux-2.6.17.13/Documentation/drivers/display/cfag12864b 2006-09-12
> 00:10:43.000000000 +0200

The patch is linewrapped :(


> +If you compiled the device as a module, don't remember to
> +update udev to get the new device node at /dev.

Why would you need to touch udev? It will handle stuff loaded at any
point in time automatically. You don't have to "update udev" at all.

> +Open the device for writing, /dev/cfag12864b0:
> +
> + int fd = open("/dev/cfag12864b0",O_WRONLY);
> +
> +Then use simple ioctl calls to control it:
> +
> + ioctl(fdisplay,CFAG12864B_IOCOFF); /* Turn off (don't clear) */
> + ioctl(fdisplay,CFAG12864B_IOCON); /* Turn on */
> + ioctl(fdisplay,CFAG12864B_IOCCLEAR); /* Clear the display */

No, no new ioctls for simple things like this please. Use sysfs or
something else.


> +Declare the matrix and other one:
> +
> + unsigned char MyDrawing[CFAG12864B_WIDTH][CFAG12864B_HEIGHT];
> +
> + unsigned char Buffer[CFAG12864B_FORMATSIZE];
> +
> +Copy the 2d matrix to the buffer , like:
> +
> + for(i=0;i<CFAG12864B_WIDTH;++i)
> + for(j=0;j<CFAG12864B_HEIGHT;++j)
> + Buffer[i+j*CFAG12864B_WIDTH]=MyDrawing[i][j];
> +
> +Call the ioctl:
> +
> + ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer);
> +
> +Voila! Your drawing should appear on the screen.

No, again use sysfs. We already have one other device in the kernel
tree that is a LCD display that works just fine with no ioctls needed at
all. Just write the text you wish to see to the sysfs file and it shows
up. Is usable from _any_ type of program, not just one that is able to
call ioctls (like shell scripts, scripting languages, command lines,
etc.)


> +You can use a copy of this header in your user-space programs.
> +
> +---
> +/*
> + * Filename: cfag12864b.h
> + * Version: 0.1.0
> + * Description: cfag12864b LCD Display Driver Header for user-space apps
> + * License: GPL

Do you really mean for your header file to be under the GPL for
userspace programs?

> linux-2.6.17.13/drivers/display/cfag12864b.c
> --- linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c 1970-01-01
> 01:00:00.000000000 +0100
> +++ linux-2.6.17.13/drivers/display/cfag12864b.c 2006-09-11
> 23:48:59.000000000 +0200
> @@ -0,0 +1,585 @@
> +/*
> + * Filename: cfag12864b.c
> + * Version: 0.1.0
> + * Description: cfag12864b LCD Display Driver
> + * License: GPL

Which version of the GPL?

> + * Depends: display ks0108
> + *
> + * Author: Miguel Ojeda Sandonis <[email protected]>
> + * Date: 2006-09-10
> + */
> +
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/display.h>
> +#include <linux/ks0108.h>
> +#include <linux/cfag12864b.h>
> +#include <asm/uaccess.h>
> +
> +#define NAME "cfag12864b"
> +#define PRINTK_PREFIX KERN_INFO NAME ": "
> +
> +
> +
> +//
> +// Device
> +//

No C++ comments please.

> +
> +static const unsigned int FirstMinor = 0;
> +static const unsigned int nDevices = 1;
> +static const char * Name = NAME;
> +
> +static int Major;
> +static dev_t FirstDevice;

No InterCaps for variable names. Please see Documentation/CodingStyle.

> +void cfag12864b_Write(
> + unsigned short _Offset,
> + unsigned char * _Buffer,
> + unsigned short _Count)

Please run the code through sparse and fix up all of the warnings it
give you.

And does this really need to be a global symbol?

> +int cfag12864b_init(void)
> +{
> + unsigned int i;
> + int Result;
> +
> + printk(PRINTK_PREFIX "Init... ");

Is this really needed?

> + Result = alloc_chrdev_region(&FirstDevice, FirstMinor, nDevices,
> Name);
> + if(Result < 0) {
> + printk("ERROR - alloc_chrdev_region\n");
> + return Result;
> + }
> + Major = MAJOR(FirstDevice);
> +
> + Devices = kmalloc(nDevices * sizeof(struct cfag12864b), GFP_KERNEL);
> + if(Devices == NULL) {
> + printk("ERROR - kmalloc\n");
> + return -ENOMEM;
> + }
> + memset(Devices, 0, nDevices * sizeof(struct cfag12864b));
> +
> + for(i=0; i<nDevices; ++i) {
> + Devices[i].Minor = FirstMinor+i;
> + Devices[i].Device = MKDEV(Major,Devices[i].Minor);
> +
> + cdev_init(&(Devices[i].CharDevice), &Fops);
> + Devices[i].CharDevice.owner = THIS_MODULE;
> + Devices[i].CharDevice.ops = &Fops;
> + Result = cdev_add(&(Devices[i].CharDevice),
> + Devices[i].Device, 1);
> + if(Result < 0) {
> + printk("ERROR - cdev_add\n");
> + kfree(Devices);
> + return Result;
> + }
> +
> + class_device_create(
> + display_class,NULL,MKDEV(Major,Devices[i].Minor),
> + NULL,"cfag12864b%d",Devices[i].Minor);

No, just use a "device_create" call, no new class_devices should be
created in the kernel, sorry. I'm working to remove it from the tree
entirely (look at the -mm tree for those patches.)

> @@ -0,0 +1,60 @@
> +/*
> + * Filename: display.c
> + * Version: 0.1.0
> + * Description: Display Class
> + * License: GPL
> + * Depends: -
> + *
> + * Author: Miguel Ojeda Sandonis <[email protected]>
> + * Date: 2006-09-10
> + */
> +
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/fs.h>
> +#include <linux/device.h>
> +#include <linux/display.h>
> +
> +#define NAME "display"
> +#define PRINTK_PREFIX KERN_INFO NAME ": "
> +
> +static char * Name = NAME;
> +
> +//
> +// Exported Display Data
> +//
> +
> +struct class * display_class;
> +EXPORT_SYMBOL_GPL(display_class);
> +
> +
> +//
> +// Module Init & Exit
> +//
> +
> +int display_init(void)
> +{
> + display_class = class_create(THIS_MODULE,Name);
> + if(IS_ERR(display_class)) {
> + printk(PRINTK_PREFIX "ERROR: class_simple_create\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +void display_exit(void)
> +{
> + class_destroy(display_class);
> +}
> +
> +module_init(display_init);
> +module_exit(display_exit);
> +
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
> +MODULE_DESCRIPTION("display");

Why do you need a whole new class for this?

"Display" is very generic, people will think it is for video stuff too.
LCD perhaps might be better?

thanks,

greg k-h

2006-09-12 05:50:26

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH V2] display: Driver ks0108 and cfag12864b

On 9/12/06, Greg KH <[email protected]> wrote:
>
> No C++ comments please.
>

I thought CodingStyle let you use them. In fact, they are C99 standard
comments, no?

> We already have one other device in the kernel [...]

I have seen them. But they are created for control the frontal LCD
panel of a PDA, smartphone...

Also, what do you mean with "write the text you want...". This screen
is graphical. The buffer is a 128*64 boolean buffer that is converted
to the LCD format and displayed on the LCD, point by point. The driver
is not made to write raw text on the screen.

For any other programs without ioctl support, you can still write to
/dev/cfag12864b%d with fwrite(), echo or whatever function do you
like. It will print the bytes you sended on the LCD, however, they
need to be in the LCD format, so you have to create a conversion
function. And such thing is _not_ likely to be done in a script,
command line,...

Anyway, the LCD display is _not_ intended to be used from scripts,
command line, or whatever. I think it doesn't make sense. :-/

(This is all in the documentation file I have written)

> No, just use a "device_create" call, no new class_devices should be
> created in the kernel, sorry. I'm working to remove it from the tree
> entirely (look at the -mm tree for those patches.)
>
> Why do you need a whole new class for this?
>
> "Display" is very generic, people will think it is for video stuff too.
> LCD perhaps might be better?
>

Well, other kind of displays could go there, not just small LCD
screens, maybe other kinds too. I have just called it "display" to
mean other screen, and the Kconfig makes clear the meaning, also, it's
defaulted to N.

Where should I put it then? char? video? ...?


I have fixed all the CodingStyle points (I think :), error printing,
init and exit functions, etc. Here is the new patch against 2.6.17.13.
Review this one, please.

Signed-off-by: Miguel Ojeda Sandonis <[email protected]>
---
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b
linux-2.6.17.13/Documentation/drivers/display/cfag12864b
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/cfag12864b 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/cfag12864b 2006-09-12
06:55:25.000000000 +0200
@@ -0,0 +1,360 @@
+ ===============================
+ cfag12864b Driver Documentation
+ ===============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+
+
+--------
+0. INDEX
+--------
+
+ 1. DEVICE INFORMATION
+ 2. WIRING
+ 3. USER-SPACE PROGRAMMING
+ 3.1. ioctl and a 128*64 boolean matrix
+ 3.2. Direct writing
+ 4. USEFUL FILES
+ 4.1. cfag12864b.h
+ 4.2. bmpwriter.h
+
+
+
+---------------------
+1. DEVICE INFORMATION
+---------------------
+
+Manufacturer: Crystalfontz
+Webpage: http://www.crystalfontz.com
+Device Webpage: http://www.crystalfontz.com/products/12864b/
+Type: LCD Display
+Width: 128
+Height: 64
+Colors: 2
+Controller: ks0108
+Controllers: 2
+Pages: 8 each controller
+Addresses: 64 each page
+
+
+
+---------
+2. WIRING
+---------
+
+The cfag12864b LCD Display Series don't have a official wiring.
+
+The common wiring is done to the parallel port:
+
+http://www.skippari.net/lcd/sekalaista/crystalfontz_cfag12864B-TMI-V.png
+
+You can get help at Crystalfontz and LCDInfo forums.
+
+
+
+-------------------------
+3. USER-SPACE PROGRAMMING
+-------------------------
+
+Include a copy of the provided header:
+
+ #include "cfag12864b.h"
+
+Open the device for writing, /dev/cfag12864b0:
+
+ int fd = open("/dev/cfag12864b0",O_WRONLY);
+
+Then use simple ioctl calls to control it:
+
+ ioctl(fdisplay,CFAG12864B_IOCOFF); /* Turn off (don't clear) */
+ ioctl(fdisplay,CFAG12864B_IOCON); /* Turn on */
+ ioctl(fdisplay,CFAG12864B_IOCCLEAR); /* Clear the display */
+
+For writing to the display, you have two options:
+
+
+3.1. ioctl & 128*64 boolean matrix
+-------------------------------------------------------
+
+This method is easier, but you have to update the entire display
+each time you want to change it.
+
+Note:
+
+ CFAG12864B_FORMATSIZE ==
+ CFAG12864B_WIDTH * CFAG12864B_HEIGHT ==
+ 128 * 64
+
+Declare the matrix and other one:
+
+ unsigned char MyDrawing[CFAG12864B_WIDTH][CFAG12864B_HEIGHT];
+
+ unsigned char Buffer[CFAG12864B_FORMATSIZE];
+
+Copy the 2d matrix to the buffer , like:
+
+ for(i=0;i<CFAG12864B_WIDTH;++i)
+ for(j=0;j<CFAG12864B_HEIGHT;++j)
+ Buffer[i+j*CFAG12864B_WIDTH]=MyDrawing[i][j];
+
+Call the ioctl:
+
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer);
+
+Voila! Your drawing should appear on the screen.
+
+
+
+3.2. Direct writing
+-------------------
+
+This methods allows you to change each byte of the device,
+so you can achieve a higher update rate.
+
+The device size is 1024 == CFAG12864B_SIZE.
+
+You can write and seek the device. The first 512 bytes write to
+the first k0108 controller (left display half) and the last 512 bytes
+write to the second ks0108 controller (right display half).
+
+Each controller is divided into 8 pages. Each page has 64 bytes.
+
+ Controller 0 Controller 1
+ _________________________
+Page 0 |____________|____________|
+Page 1 |____________|____________|
+Page 2 |____________|____________|
+Page 3 |____________|____________|
+Page 4 |____________|____________|
+Page 5 |____________|____________|
+Page 6 |____________|____________|
+Page 7 |____________|____________|
+ <--- 64 --->
+
+You will understand how the device work executing some commands:
+
+ # echo -n A > /dev/cfag12864b0
+ # echo -n a > /dev/cfag12864b0
+ # echo AAAAAA > /dev/cfag12864b0
+ # echo 000000 > /dev/cfag12864b0
+ # echo Hello world! > /dev/cfag12864b0
+ # echo Hello world! Hello world! > /dev/cfag12864b0
+
+After you understand it, code your functions to change specific bytes.
+
+Use write() and lseek() system calls, like:
+
+ lseek(fdisplay,ipage*CFAG12864B_HEIGHT,SEEK_SET);
+ lseek(fdisplay,icontroller*CFAG12864B_SIZE/2,SEEK_SET);
+
+ write(fdisplay,bufpage,CFAG12864B_HEIGHT);
+ write(fdisplay,bufcontroller,CFAG12864B_SIZE/2);
+ write(fdisplay,bufdisplay,CFAG12864B_SIZE);
+
+
+---------------
+4. USEFUL FILES
+---------------
+
+
+4.1 cfag12864b.h
+----------------
+
+You can use a copy of this header in your user-space programs.
+
+---
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header for user-space apps
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-10
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <sys/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_FORMATSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+#define CFAG12864B_SIZE 1024
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+#endif // _CFAG12864B_H_
+---
+
+
+
+4.2 Example BMP writer
+----------------------
+
+You can take ideas from this code. It reads a .bmp file,
+convert it to a boolean [128*64] buffer and then use
+ioctl to display it on the screen.
+
+---
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "cfag12864b.h"
+
+#define BMP_SIZE 1024
+
+union dword
+{
+ unsigned int u32;
+ unsigned char u8[4];
+};
+
+#define Bit(n) ((unsigned char)(1<<(n)))
+
+void BMP2Format(
+ unsigned char _Src[BMP_SIZE],
+ unsigned char _Dest[CFAG12864B_FORMATSIZE])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Bits = 8;
+
+ unsigned int Y,X,Bit;
+
+ for(Y=0; Y<Height; ++Y)
+ for(X=0; X<Width/Bits; ++X)
+ for(Bit=0; Bit<Bits; ++Bit)
+ _Dest[X*Bits+Bit+(Height-Y-1)*Width] =
+ _Src[Y*Width/Bits+X]&Bit(Bits-Bit-1)?0:1;
+}
+
+int main(int argc, char * argv[])
+{
+ const unsigned int Width = CFAG12864B_WIDTH;
+ const unsigned int Height = CFAG12864B_HEIGHT;
+ const unsigned int Size = CFAG12864B_SIZE;
+ const unsigned int BPP = 1;
+ const unsigned int HeaderSize = 0x3E;
+ const unsigned int BMPSize = BMP_SIZE;
+
+ unsigned char c;
+ unsigned int i,j;
+ union dword n;
+
+ unsigned char Buffer_BMP[BMP_SIZE];
+ unsigned char Buffer_Matrix[CFAG12864B_FORMATSIZE];
+
+ int fdisplay;
+ FILE * fbmp;
+
+ // Check args
+ if(argc!=3) {
+ printf("%s: Bad number of arguments. Expected 3\n",
+ argv[0]);
+ return -1;
+ }
+
+ // Open file
+ fbmp = fopen(argv[2],"rb");
+ if(fbmp==NULL) {
+ printf("%s: Can't open %s\n",argv[0], argv[2]);
+ return -2;
+ }
+
+ // Check file size
+ fseek(fbmp,0,SEEK_END);
+ i=ftell(fbmp);
+ if(i!=HeaderSize+Size) {
+ printf("%s: Bad file size. %i instead of %i\n",
+ argv[0], i, HeaderSize+Size);
+ fclose(fbmp);
+ return -3;
+ }
+
+ // Check both magic BMP bytes
+ fseek(fbmp,0,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!='B') {
+ printf("%s: Bad first magic byte. '%c' instead of 'B'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -4;
+ }
+ c = fgetc(fbmp);
+ if(c!='M') {
+ printf("%s: Bad second magic byte. '%c' instead of 'M'\n",
+ argv[0], c);
+ fclose(fbmp);
+ return -5;
+ }
+
+ // Check this is a 128x64 1-bpp BMP file
+ fseek(fbmp,0x12,SEEK_SET);
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Width) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Width);
+ fclose(fbmp);
+ return -6;
+ }
+ for(i=0; i<4; ++i)
+ n.u8[i] = fgetc(fbmp);
+ if(n.u32!=Height) {
+ printf("%s: Bad width. %i instead of %i\n",
+ argv[0], n.u32, Height);
+ fclose(fbmp);
+ return -7;
+ }
+ fseek(fbmp,0x1C,SEEK_SET);
+ c = fgetc(fbmp);
+ if(c!=BPP) {
+ printf("%s: Bad bpp. %i instead of %i\n",
+ argv[0], c, BPP);
+ fclose(fbmp);
+ return -8;
+ }
+
+ // Get bitmap data
+ fseek(fbmp,0x3E,SEEK_SET);
+ for(i=0; i<BMPSize; ++i)
+ Buffer_BMP[i]=fgetc(fbmp);
+ fclose(fbmp);
+
+ // Transform BMP data to 2D matrix
+ BMP2Format(Buffer_BMP,Buffer_Matrix);
+
+ // Open file
+ fdisplay = open(argv[1],O_WRONLY);
+ if(fdisplay < 0) {
+ printf("%s: Can't open %s\n", argv[0], argv[1]);
+ return -9;
+ }
+
+ // Send matrix
+ ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer_Matrix);
+
+ // Close file
+ close(fdisplay);
+
+ return 0;
+}
+---
+
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/CREDITS linux-2.6.17.13/CREDITS
--- linux-2.6.17.13-vanilla/CREDITS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/CREDITS 2006-09-12 00:16:06.000000000 +0200
@@ -2534,6 +2534,14 @@ S: Subiaco, 6008
S: Perth, Western Australia
S: Australia

+N: Miguel Ojeda Sandonis
+E: [email protected]
+D: Author: LCD Display Drivers (ks0108, cfag12864b)
+D: Maintainer: LCD Display Drivers Tree (drivers/display/*)
+S: C/ Mieses 20, 9-B
+S: Valladolid 47009
+S: Spain
+
N: Greg Page
E: [email protected]
D: IPX development and support
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/drivers/display/display
linux-2.6.17.13/Documentation/drivers/display/display
--- linux-2.6.17.13-vanilla/Documentation/drivers/display/display 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/Documentation/drivers/display/display 2006-09-12
04:53:09.000000000 +0200
@@ -0,0 +1,37 @@
+ =============================
+ Display Drivers Documentation
+ =============================
+
+License: GPL
+Author & Maintainer: Miguel Ojeda Sandonis <[email protected]>
+Date: 2006-09-10
+
+--------
+0. INDEX
+--------
+
+ 1. NEW DISPLAY DRIVERS
+ 2. GENERAL TIPS
+
+
+
+----------------------
+1. NEW DISPLAY DRIVERS
+----------------------
+
+Feel free to send me new display drivers. I will try to do my best.
+
+If you don't get any answer, send your patch directly to the linux-kernel ml.
+
+
+
+---------------
+2. GENERAL TIPS
+---------------
+
+- Divide your driver into the controller driver, like ks0108,
+ and the specific LCD display series driver, like cfag12864b.
+
+- Claim for your IO ports in the controller driver.
+
+EOF
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt
linux-2.6.17.13/Documentation/ioctl-number.txt
--- linux-2.6.17.13-vanilla/Documentation/ioctl-number.txt 2006-09-09
05:23:25.000000000 +0200
+++ linux-2.6.17.13/Documentation/ioctl-number.txt 2006-09-12
04:53:09.000000000 +0200
@@ -190,3 +190,5 @@ Code Seq# Include File Comments
<mailto:[email protected]>
0xF3 00-3F video/sisfb.h sisfb (in development)
<mailto:[email protected]>
+0xFF 00-1F cfag12864b LCD Display linux/cfag12864b.h
+ <mailto:[email protected]>
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c
linux-2.6.17.13/drivers/display/cfag12864b.c
--- linux-2.6.17.13-vanilla/drivers/display/cfag12864b.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/cfag12864b.c 2006-09-12
07:14:50.000000000 +0200
@@ -0,0 +1,566 @@
+/*
+ * Filename: cfag12864b.c
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver
+ * License: GPL
+ * Depends: display ks0108
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/display.h>
+#include <linux/ks0108.h>
+#include <linux/cfag12864b.h>
+#include <asm/uaccess.h>
+
+#define NAME "cfag12864b"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Device
+//
+
+static const unsigned int cfag12864b_firstminor = 0;
+static const unsigned int cfag12864b_ndevices = 1;
+static const char * cfag12864b_name = NAME;
+
+static int cfag12864b_major;
+static int cfag12864b_minor;
+static dev_t cfag12864b_device;
+struct cdev cfag12864b_chardevice;
+
+
+
+
+//
+// cfag12864b Commands
+//
+
+#define bit(n) ((unsigned char)(1<<(n)))
+#define nobit(n) ((unsigned char)(~bit(n)))
+static const unsigned int cfag12864b_bits = 8;
+
+static const unsigned int cfag12864b_width = CFAG12864B_WIDTH;
+static const unsigned int cfag12864b_height = CFAG12864B_HEIGHT;
+static const unsigned int cfag12864b_matrixsize = CFAG12864B_MATRIXSIZE;
+static const unsigned int cfag12864b_controllers = CFAG12864B_CONTROLLERS;
+static const unsigned int cfag12864b_pages = CFAG12864B_PAGES;
+static const unsigned int cfag12864b_addresses = CFAG12864B_ADDRESSES;
+static const unsigned int cfag12864b_size = CFAG12864B_SIZE;
+
+static unsigned char cfag12864b_state = 0;
+
+static void cfag12864b_set(void)
+{
+ ks0108_writecontrol(cfag12864b_state);
+}
+
+static void cfag12864b_e(unsigned char state)
+{
+ if(state)
+ cfag12864b_state |= bit(0);
+ else
+ cfag12864b_state &= nobit(0);
+ cfag12864b_set();
+}
+
+static void cfag12864b_cs1(unsigned char state)
+{
+ if(state)
+ cfag12864b_state |= bit(2);
+ else
+ cfag12864b_state &= nobit(2);
+ cfag12864b_set();
+}
+
+static void cfag12864b_cs2(unsigned char state)
+{
+ if(state)
+ cfag12864b_state |= bit(1);
+ else
+ cfag12864b_state &= nobit(1);
+ cfag12864b_set();
+}
+
+static void cfag12864b_di(unsigned char state)
+{
+ if(state)
+ cfag12864b_state |= bit(3);
+ else
+ cfag12864b_state &= nobit(3);
+ cfag12864b_set();
+}
+
+static void cfag12864b_firstcontroller(unsigned char state)
+{
+ if(state)
+ cfag12864b_cs1(0);
+ else
+ cfag12864b_cs1(1);
+}
+
+static void cfag12864b_secondcontroller(unsigned char state)
+{
+ if(state)
+ cfag12864b_cs2(0);
+ else
+ cfag12864b_cs2(1);
+}
+
+static void cfag12864b_setcontrollers(unsigned char first, unsigned
char second)
+{
+ cfag12864b_firstcontroller(first);
+ cfag12864b_secondcontroller(second);
+}
+
+static void cfag12864b_controller(unsigned char which)
+{
+ if(which==0)
+ cfag12864b_setcontrollers(1,0);
+ else if(which==1)
+ cfag12864b_setcontrollers(0,1);
+}
+
+static void cfag12864b_displaystate(unsigned char state)
+{
+ cfag12864b_di(0);
+ cfag12864b_e(1);
+ ks0108_displaystate(state);
+ cfag12864b_e(0);
+}
+
+static void cfag12864b_address(unsigned char address)
+{
+ cfag12864b_di(0);
+ cfag12864b_e(1);
+ ks0108_address(address);
+ cfag12864b_e(0);
+}
+
+static void cfag12864b_page(unsigned char page)
+{
+ cfag12864b_di(0);
+ cfag12864b_e(1);
+ ks0108_page(page);
+ cfag12864b_e(0);
+}
+
+static void cfag12864b_startline(unsigned char startline)
+{
+ cfag12864b_di(0);
+ cfag12864b_e(1);
+ ks0108_startline(startline);
+ cfag12864b_e(0);
+}
+
+static void cfag12864b_writebyte(unsigned char byte)
+{
+ cfag12864b_di(1);
+ cfag12864b_e(1);
+ ks0108_writedata(byte);
+ cfag12864b_e(0);
+}
+
+static void cfag12864b_nop(void)
+{
+ cfag12864b_startline(0);
+}
+
+
+
+//
+// Auxiliar
+//
+
+static void normalizeoffset(unsigned int * offset)
+{
+ if(*offset>=cfag12864b_pages*cfag12864b_addresses)
+ *offset-=cfag12864b_pages*cfag12864b_addresses;
+}
+
+static unsigned char calcaddress(unsigned int offset)
+{
+ normalizeoffset(&offset);
+ return offset%cfag12864b_addresses;
+}
+
+static unsigned char calccontroller(unsigned int offset)
+{
+ if(offset<cfag12864b_pages*cfag12864b_addresses)
+ return 0;
+ return 1;
+}
+
+static unsigned char calcpage(unsigned int offset)
+{
+ normalizeoffset(&offset);
+ return offset/cfag12864b_addresses;
+}
+
+
+
+//
+// cfag12864b Exported Commands
+//
+
+void cfag12864b_on(void)
+{
+ cfag12864b_setcontrollers(1,1);
+ cfag12864b_displaystate(1);
+}
+
+void cfag12864b_off(void)
+{
+ cfag12864b_setcontrollers(1,1);
+ cfag12864b_displaystate(0);
+}
+
+void cfag12864b_clear(void)
+{
+ unsigned char page,address;
+
+ cfag12864b_setcontrollers(1,1);
+ for(page=0; page<cfag12864b_pages; ++page) {
+ cfag12864b_page(page);
+ cfag12864b_address(0);
+ for(address=0; address<cfag12864b_addresses; ++address)
+ cfag12864b_writebyte(0);
+ }
+}
+
+void cfag12864b_write(
+ unsigned short offset,
+ unsigned char * buffer,
+ unsigned short count)
+{
+ unsigned short i;
+
+ // Invalid values: They get updated at the first cycle
+ unsigned char controller = 0xFF;
+ unsigned char page = 0xFF;
+ unsigned char address = 0xFF;
+
+ unsigned char tmpcontroller, tmppage, tmpaddress;
+
+ for(i=0; i<count; ++i, ++offset, ++address) {
+ tmpcontroller = calccontroller(offset);
+ tmppage = calcpage(offset);
+ tmpaddress = calcaddress(offset);
+
+ if(controller != tmpcontroller) {
+ controller = tmpcontroller;
+ cfag12864b_controller(controller);
+ cfag12864b_nop();
+ }
+ if(page != tmppage) {
+ page = tmppage;
+ cfag12864b_page(page);
+ cfag12864b_nop();
+ }
+
+ /*if(address != tmpaddress) {
+ address = tmpaddress;
+ cfag12864b_address(address);
+ cfag12864b_nop();
+ }*/
+
+ /*if(tmpcontroller == 0) {
+ if(address != tmpaddress) {
+ address = tmpaddress;
+ cfag12864b_address(address);
+ }
+ }
+ else {
+ cfag12864b_address(tmpaddress);
+ cfag12864b_nop();
+ }*/
+
+ // Safe method, still quick
+ cfag12864b_address(tmpaddress);
+ cfag12864b_nop();
+
+ // Dummy
+ cfag12864b_nop();
+
+ cfag12864b_writebyte(buffer[i]);
+ }
+}
+
+void cfag12864b_format(unsigned char * src)
+{
+ unsigned short controller,page,address,bit;
+ unsigned char * dest;
+
+ dest = kmalloc(sizeof(unsigned char)*cfag12864b_size,GFP_KERNEL);
+ if(dest == NULL) {
+ printk(PRINTK_PREFIX "format: ERROR: "
+ "can't alloc memory %i bytes\n",
+ sizeof(unsigned char)*cfag12864b_size);
+ return;
+ }
+
+ for(controller=0; controller<cfag12864b_controllers; ++controller)
+ for(page=0; page<cfag12864b_pages; ++page)
+ for(address=0; address<cfag12864b_addresses; ++address) {
+ dest[(controller*cfag12864b_pages+page)*cfag12864b_addresses+address]=0;
+ for(bit=0; bit<cfag12864b_bits; ++bit)
+ if(src[controller*cfag12864b_addresses+address+(page*cfag12864b_bits+bit)*cfag12864b_width])
+ dest[(controller*cfag12864b_pages+page)*cfag12864b_addresses+address]|=bit(bit);
+ }
+
+ cfag12864b_write(0,dest,cfag12864b_size);
+
+ kfree(dest);
+}
+
+EXPORT_SYMBOL_GPL(cfag12864b_on);
+EXPORT_SYMBOL_GPL(cfag12864b_off);
+EXPORT_SYMBOL_GPL(cfag12864b_clear);
+EXPORT_SYMBOL_GPL(cfag12864b_write);
+EXPORT_SYMBOL_GPL(cfag12864b_format);
+
+
+//
+// cfag12864b_fops
+//
+
+static loff_t cfag12864b_fopseek(
+ struct file * filp,
+ loff_t offset,
+ int whence)
+{
+ loff_t new;
+
+ switch(whence) {
+ case 0: // SEEK_SET
+ new = offset;
+ break;
+ case 1: // SEEK_CUR
+ new = filp->f_pos + offset;
+ break;
+ case 2: // SEEK_END
+ new = cfag12864b_size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if(new < 0)
+ return -EINVAL;
+ filp->f_pos = new;
+ return new;
+}
+
+
+static ssize_t cfag12864b_fopwrite(
+ struct file * filp,
+ const char __user * buffer,
+ size_t count,
+ loff_t * offset)
+{
+ int ret = -EINVAL;
+ int result;
+ char * tmpbuffer;
+
+ if(*offset>cfag12864b_size)
+ return 0;
+ if(*offset+count>cfag12864b_size)
+ count=cfag12864b_size-*offset;
+
+ tmpbuffer = kmalloc(count,GFP_KERNEL);
+ if(tmpbuffer == NULL) {
+ printk(PRINTK_PREFIX "FOP write: ERROR: "
+ "can't alloc memory %i bytes\n",count);
+ ret = -ENOMEM;
+ goto none;
+ }
+
+ result = copy_from_user(tmpbuffer, buffer, count);
+ if(result != 0) {
+ printk(PRINTK_PREFIX "FOP write: ERROR: "
+ "can't copy memory from user\n");
+ ret = -EFAULT;
+ goto bufferalloced;
+ }
+
+ cfag12864b_write(*offset, tmpbuffer, count);
+
+ *offset += count;
+ ret = count;
+
+bufferalloced:
+ kfree(tmpbuffer);
+
+none:
+ return ret;
+}
+
+static int cfag12864b_fopioctlformat(unsigned long arg)
+{
+ int result;
+ int ret = -ENOTTY;
+
+ unsigned char * tmpbuffer;
+
+ tmpbuffer = kmalloc(
+ sizeof(unsigned char)*cfag12864b_matrixsize,GFP_KERNEL);
+ if(tmpbuffer == NULL) {
+ printk(PRINTK_PREFIX "FOP ioctl: ERROR: "
+ "can't alloc memory %i bytes\n",
+ sizeof(unsigned char)*cfag12864b_matrixsize);
+ goto none;
+ }
+
+ result = copy_from_user(
+ tmpbuffer,
+ (void __user *)arg,
+ sizeof(unsigned char)*cfag12864b_matrixsize);
+ if(result != 0) {
+ printk(PRINTK_PREFIX "FOP ioctl: ERROR: "
+ "can't copy memory from user\n");
+ goto bufferalloced;
+ }
+
+ cfag12864b_format(tmpbuffer);
+
+ ret = 0;
+
+bufferalloced:
+ kfree(tmpbuffer);
+
+none:
+ return ret;
+}
+
+static int cfag12864b_fopioctl(
+ struct inode * inode,
+ struct file * filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ if(_IOC_TYPE(cmd) != CFAG12864B_IOC_MAGIC)
+ return -ENOTTY;
+ if(_IOC_NR(cmd) > CFAG12864B_IOC_MAXNR)
+ return -ENOTTY;
+
+ switch(cmd) {
+ case CFAG12864B_IOCON:
+ cfag12864b_on();
+ break;
+ case CFAG12864B_IOCOFF:
+ cfag12864b_off();
+ break;
+ case CFAG12864B_IOCCLEAR:
+ cfag12864b_clear();
+ break;
+ case CFAG12864B_IOCFORMAT:
+ return cfag12864b_fopioctlformat(arg);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+
+
+static struct file_operations cfag12864b_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = cfag12864b_fopseek,
+ .write = cfag12864b_fopwrite,
+ .ioctl = cfag12864b_fopioctl,
+};
+
+
+
+
+
+
+
+
+
+//
+// Module Init & Exit
+//
+
+static int __init cfag12864b_init_module(void)
+{
+ int result;
+ int ret = -EINVAL;
+
+ result = alloc_chrdev_region(
+ &cfag12864b_device, cfag12864b_firstminor,
+ cfag12864b_ndevices, cfag12864b_name);
+ if(result < 0) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "can't alloc the char device region\n");
+ ret = result;
+ goto none;
+ }
+
+ cfag12864b_major = MAJOR(cfag12864b_device);
+ cfag12864b_minor = cfag12864b_firstminor;
+ cfag12864b_device = MKDEV(cfag12864b_major,cfag12864b_minor);
+
+ cfag12864b_clear();
+ cfag12864b_on();
+
+ cdev_init(&cfag12864b_chardevice,&cfag12864b_fops);
+ cfag12864b_chardevice.owner = THIS_MODULE;
+ cfag12864b_chardevice.ops = &cfag12864b_fops;
+ result = cdev_add(
+ &cfag12864b_chardevice,
+ cfag12864b_device, cfag12864b_ndevices);
+ if(result < 0) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "unable to add a new char device\n");
+ ret = result;
+ goto regionalloced;
+ }
+
+ class_device_create(
+ display_class,NULL,
+ cfag12864b_device,
+ NULL,"cfag12864b%d",cfag12864b_minor);
+
+ printk(PRINTK_PREFIX "Inited\n");
+
+ return 0;
+
+regionalloced:
+ unregister_chrdev_region(cfag12864b_device, cfag12864b_ndevices);
+
+none:
+ return ret;
+}
+
+static void cfag12864b_exit_module(void)
+{
+ cfag12864b_off();
+
+ class_device_destroy(display_class,cfag12864b_device);
+ cdev_del(&cfag12864b_chardevice);
+ unregister_chrdev_region(cfag12864b_device, cfag12864b_ndevices);
+
+ printk(PRINTK_PREFIX "Exited\n");
+}
+
+module_init(cfag12864b_init_module);
+module_exit(cfag12864b_exit_module);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("cfag12864b");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/display.c
linux-2.6.17.13/drivers/display/display.c
--- linux-2.6.17.13-vanilla/drivers/display/display.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/display.c 2006-09-12
06:24:12.000000000 +0200
@@ -0,0 +1,70 @@
+/*
+ * Filename: display.c
+ * Version: 0.1.0
+ * Description: Display Class
+ * License: GPL
+ * Depends: -
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/display.h>
+
+#define NAME "display"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+static char * display_name = NAME;
+
+//
+// Exported Display Data
+//
+
+struct class * display_class;
+EXPORT_SYMBOL_GPL(display_class);
+
+
+//
+// Module Init & Exit
+//
+
+static int __init display_init_module(void)
+{
+ int ret = -EINVAL;
+
+ display_class = class_create(THIS_MODULE,display_name);
+ if(IS_ERR(display_class)) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "can't create %s class\n",display_name);
+ goto none;
+ }
+
+ printk(PRINTK_PREFIX "Inited\n");
+
+ return 0;
+
+none:
+ return ret;
+}
+
+static void __exit display_exit_module(void)
+{
+ class_destroy(display_class);
+
+ printk(PRINTK_PREFIX "Exited\n");
+}
+
+module_init(display_init_module);
+module_exit(display_exit_module);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("display");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Kconfig
linux-2.6.17.13/drivers/display/Kconfig
--- linux-2.6.17.13-vanilla/drivers/display/Kconfig 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Kconfig 2006-09-12 04:53:09.000000000 +0200
@@ -0,0 +1,61 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+# Display drivers configuration.
+#
+
+menu "Display support"
+
+config DISPLAY
+ tristate "Display support"
+ default n
+ ---help---
+ If you have an extra display, like a small LCD screen, say Y.
+
+ To compile this as a module, choose M here:
+ module will be called display.
+
+ If unsure, say N.
+
+comment "Parallel port dependent:"
+
+config KS0108
+ tristate "KS0108 LCD Controller"
+ depends on DISPLAY && PARPORT
+ default n
+ ---help---
+ If you have a LCD display controlled by one or more KS0108
+ controllers, say Y. You will need also another more specific
+ driver for your LCD.
+
+ Depends on Parallel Port support. If you say Y at
+ parport, you will be able to compile this as a module (M)
+ and built-in as well (Y). If you said M at parport,
+ you will be able only to compile this as a module (M).
+
+ To compile this as a module, choose M here:
+ module will be called ks0108.
+
+ If unsure, say N.
+
+config CFAG12864B
+ tristate "CFAG12864B LCD Display"
+ depends on KS0108
+ default n
+ ---help---
+ If you have a Crystalfontz 128x64 2-color LCD display,
+ cfag12864b Series, say Y. You also need the ks0108 LCD
+ Controller driver.
+
+ For help about how to wire your LCD to the parallel port,
+ check this image: http://www.skippari.net/lcd/sekalaista
+ /crystalfontz_cfag12864B-TMI-V.png
+
+ To compile this as a module, choose M here:
+ module will be called cfag12864b.
+
+ If unsure, say N.
+
+endmenu
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/ks0108.c
linux-2.6.17.13/drivers/display/ks0108.c
--- linux-2.6.17.13-vanilla/drivers/display/ks0108.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/ks0108.c 2006-09-12 07:13:29.000000000 +0200
@@ -0,0 +1,175 @@
+/*
+ * Filename: ks0108.c
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver
+ * License: GPL
+ * Depends: parport
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/parport.h>
+#include <linux/ks0108.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+#define NAME "ks0108"
+#define PRINTK_PREFIX KERN_INFO NAME ": "
+
+
+
+//
+// Module
+//
+
+static unsigned int ks0108_port = 0x378;
+module_param(ks0108_port,uint,S_IRUGO);
+
+
+
+//
+// Device
+//
+
+static const char * ks0108_name = NAME;
+
+static struct parport * ks0108_parport;
+static struct pardevice * ks0108_pardevice;
+
+
+
+//
+// ks0108 Exported cmds
+//
+
+#define bit(n) ((unsigned char)(1<<(n)))
+
+void ks0108_writedata(unsigned char byte)
+{
+ parport_write_data(ks0108_parport,byte);
+}
+
+void ks0108_writecontrol(unsigned char byte)
+{
+ const unsigned int ecycledelay = 2;
+ udelay(ecycledelay);
+ parport_write_control(ks0108_parport,byte^(bit(3)|bit(1)|bit(0)));
+}
+
+void ks0108_displaystate(unsigned char state)
+{
+ unsigned char cmd = bit(1) | bit(2) | bit(3) | bit(4) | bit(5);
+ if(state)
+ cmd |= bit(0);
+ ks0108_writedata(cmd);
+}
+
+void ks0108_startline(unsigned char startline)
+{
+ const unsigned char maxstartline = 63;
+ unsigned char cmd = bit(6) | bit(7);
+ if(startline>maxstartline)
+ startline=maxstartline;
+ cmd |= startline;
+ ks0108_writedata(cmd);
+}
+
+void ks0108_address(unsigned char address)
+{
+ const unsigned char maxaddress = 63;
+ unsigned char cmd = bit(6);
+ if(address>maxaddress)
+ address=maxaddress;
+ cmd |= address;
+ ks0108_writedata(cmd);
+}
+
+void ks0108_page(unsigned char page)
+{
+ const unsigned char maxpage = 7;
+ unsigned char cmd = bit(3) | bit(4) | bit(5) | bit(7);
+ if(page>maxpage)
+ page=maxpage;
+ cmd |= page;
+ ks0108_writedata(cmd);
+}
+
+
+EXPORT_SYMBOL_GPL(ks0108_writedata);
+EXPORT_SYMBOL_GPL(ks0108_writecontrol);
+EXPORT_SYMBOL_GPL(ks0108_displaystate);
+EXPORT_SYMBOL_GPL(ks0108_startline);
+EXPORT_SYMBOL_GPL(ks0108_address);
+EXPORT_SYMBOL_GPL(ks0108_page);
+
+
+
+
+//
+// Module Init & Exit
+//
+
+static int __init ks0108_init_module(void)
+{
+ int result;
+ int ret = -EINVAL;
+
+ ks0108_parport = parport_find_base(ks0108_port);
+ if(ks0108_parport == NULL) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "parport didn't find %i port\n",ks0108_port);
+ goto none;
+ }
+
+ ks0108_pardevice = parport_register_device(
+ ks0108_parport,ks0108_name,
+ NULL,NULL,NULL,
+ PARPORT_DEV_EXCL,NULL);
+ if(ks0108_pardevice == NULL) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "parport didn't register new device\n");
+ goto none;
+ }
+
+ result = parport_claim(ks0108_pardevice);
+ if(result != 0) {
+ printk(PRINTK_PREFIX "ERROR: "
+ "can't claim %i parport, maybe in use\n",ks0108_port);
+ ret = result;
+ goto registered;
+ }
+
+ printk(PRINTK_PREFIX "Inited\n");
+ return 0;
+
+registered:
+ parport_unregister_device(ks0108_pardevice);
+
+none:
+ return ret;
+}
+
+static void __exit ks0108_exit_module(void)
+{
+ parport_release(ks0108_pardevice);
+ parport_unregister_device(ks0108_pardevice);
+
+ printk(PRINTK_PREFIX "Exited\n");
+}
+
+module_init(ks0108_init_module);
+module_exit(ks0108_exit_module);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <[email protected]>");
+MODULE_DESCRIPTION("ks0108");
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/display/Makefile
linux-2.6.17.13/drivers/display/Makefile
--- linux-2.6.17.13-vanilla/drivers/display/Makefile 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/drivers/display/Makefile 2006-09-12 04:53:09.000000000 +0200
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel Display device drivers.
+#
+
+obj-$(CONFIG_DISPLAY) += display.o
+obj-$(CONFIG_KS0108) += ks0108.o
+obj-$(CONFIG_CFAG12864B) += cfag12864b.o
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Kconfig
linux-2.6.17.13/drivers/Kconfig
--- linux-2.6.17.13-vanilla/drivers/Kconfig 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Kconfig 2006-09-12 04:53:09.000000000 +0200
@@ -72,4 +72,8 @@ source "drivers/edac/Kconfig"

source "drivers/rtc/Kconfig"

+# parport before display - some displays depend on it.
+
+source "drivers/display/Kconfig"
+
endmenu
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/drivers/Makefile
linux-2.6.17.13/drivers/Makefile
--- linux-2.6.17.13-vanilla/drivers/Makefile 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/drivers/Makefile 2006-09-12 04:53:09.000000000 +0200
@@ -74,3 +74,4 @@ obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
+obj-$(CONFIG_DISPLAY) += display/
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/cfag12864b.h
linux-2.6.17.13/include/linux/cfag12864b.h
--- linux-2.6.17.13-vanilla/include/linux/cfag12864b.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/cfag12864b.h 2006-09-12
05:18:57.000000000 +0200
@@ -0,0 +1,45 @@
+/*
+ * Filename: cfag12864b.h
+ * Version: 0.1.0
+ * Description: cfag12864b LCD Display Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+#ifndef _CFAG12864B_H_
+#define _CFAG12864B_H_
+
+#include <linux/ioctl.h>
+
+#define CFAG12864B_WIDTH 128
+#define CFAG12864B_HEIGHT 64
+#define CFAG12864B_MATRIXSIZE CFAG12864B_WIDTH*CFAG12864B_HEIGHT
+
+#define CFAG12864B_CONTROLLERS 2
+#define CFAG12864B_PAGES 8
+#define CFAG12864B_ADDRESSES 64
+#define CFAG12864B_SIZE CFAG12864B_CONTROLLERS * \
+ CFAG12864B_PAGES * \
+ CFAG12864B_ADDRESSES
+
+#define CFAG12864B_IOC_MAGIC 0xFF
+#define CFAG12864B_IOC_MAXNR 0x03
+
+#define CFAG12864B_IOCOFF _IO(CFAG12864B_IOC_MAGIC,0)
+#define CFAG12864B_IOCON _IO(CFAG12864B_IOC_MAGIC,1)
+#define CFAG12864B_IOCCLEAR _IO(CFAG12864B_IOC_MAGIC,2)
+#define CFAG12864B_IOCFORMAT _IOW(CFAG12864B_IOC_MAGIC,3,void *)
+
+extern void cfag12864b_on(void);
+extern void cfag12864b_off(void);
+extern void cfag12864b_clear(void);
+extern void cfag12864b_write(
+ unsigned short offset,
+ unsigned char * buffer,
+ unsigned short count);
+extern void cfag12864b_format(unsigned char * src);
+
+#endif // _CFAG12864B_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/display.h
linux-2.6.17.13/include/linux/display.h
--- linux-2.6.17.13-vanilla/include/linux/display.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/display.h 2006-09-12 05:18:47.000000000 +0200
@@ -0,0 +1,19 @@
+/*
+ * Filename: display.h
+ * Version: 0.1.0
+ * Description: Display Class Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include <linux/device.h>
+
+extern struct class * display_class;
+
+#endif // _DISPLAY_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/include/linux/ks0108.h
linux-2.6.17.13/include/linux/ks0108.h
--- linux-2.6.17.13-vanilla/include/linux/ks0108.h 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.17.13/include/linux/ks0108.h 2006-09-12 05:18:25.000000000 +0200
@@ -0,0 +1,22 @@
+/*
+ * Filename: ks0108.h
+ * Version: 0.1.0
+ * Description: ks0108 LCD Display Controller Driver Header
+ * License: GPL
+ *
+ * Author: Miguel Ojeda Sandonis <[email protected]>
+ * Date: 2006-09-12
+ */
+
+#ifndef _KS0108_H_
+#define _KS0108_H_
+
+extern void ks0108_writedata(unsigned char byte);
+extern void ks0108_writecontrol(unsigned char byte);
+extern void ks0108_displaystate(unsigned char state);
+extern void ks0108_startline(unsigned char startline);
+extern void ks0108_address(unsigned char address);
+extern void ks0108_page(unsigned char page);
+
+#endif // _KS0108_H_
+
diff -uprN -X linux-2.6.17.13-vanilla/Documentation/dontdiff
linux-2.6.17.13-vanilla/MAINTAINERS linux-2.6.17.13/MAINTAINERS
--- linux-2.6.17.13-vanilla/MAINTAINERS 2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13/MAINTAINERS 2006-09-12 04:53:09.000000000 +0200
@@ -1640,6 +1640,11 @@ M: [email protected]
L: [email protected]
S: Maintained

+DISPLAY DRIVERS
+P: Miguel Ojeda Sandonis
+M: [email protected]
+S: Maintained
+
LED SUBSYSTEM
P: Richard Purdie
M: [email protected]

2006-09-12 06:09:34

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH 2.6.17.13] display: Driver ks0108 and cfag12864b

On 9/12/06, Greg KH <[email protected]> wrote:
>
> The patch is linewrapped :(
>

Please tell me if the next one is linewrapped too.

>
> Why would you need to touch udev? It will handle stuff loaded at any
> point in time automatically. You don't have to "update udev" at all.
>

My fault. My firsts tests were on a Vector Linux, and, I don't know
why (bad cfg I guess), but udev didn't create the nodes automatically.
Now, in Debian, it does. I have noticed that and the last patch
doesn't have such lines. Sorry.

>
> Do you really mean for your header file to be under the GPL for
> userspace programs?
>

Ups, it is just an example for a user-space program that wants to use
the cfag12864b. I will remove the License line anyway.


>
> Which version of the GPL?
>

The same as the kernel, I think. Is that right?

> [about useless printk's]
> Is this really needed?
>

It isn't anymore. In the patch it is also removed and all the printks
improved, as Alexey Dobriyan told me also to do.

> "Display" is very generic, people will think it is for video stuff too.
> LCD perhaps might be better?
>

I thought the same, but LCD is already used by the "PDA Frontal LCD
Panel". They got the "linux/lcd.h" and "drivers/lcd/*", as well as a
fixed major number.

So I thought "bigger" and, IMHO a new folder for this kind of
misc-secondary-display devices (LCD or not) will be fine.

I think (maybe I'm wrong but...), we can just put the drivers right
there. There are a lot of this kind of hardware, but it will take time
to code more drivers. So if the resulting drivers are so different or
the drivers/* get a major update, relocate them. If not, leave them
until we have enough samples to start classifying.