Compare commits

...

1 Commits

Author SHA1 Message Date
Markus Koch 5226513971 display:kernel-driver: commit old work-in-progress code
Originally, I intended to do everything over SPI.
The new hardware, however, uses UART for the keyboard
and the printer, so this code is obsolete and the
display driver is pretty much complete the way it is
in the last commit.
2019-10-26 16:58:31 +02:00
3 changed files with 257 additions and 2 deletions

View File

@ -0,0 +1,30 @@
&pio {
leds_opc: led_pins {
pins = "PA15";
function = "gpio_out";
};
lmg6202ulyt_opc: lmg6202ulyt_pins {
pins = "PA12", "PA3";
function = "gpio_in"; /* initial configuration */
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
};
};
&spi0 {
status = "okay";
lmg6202ulyt@0 {
compatible = "hitachi,lmg6202ulyt";
reg = <0x0>;
spi-max-frequency = <2000000>;
cs-gpios = <0>, <&pio 0 3 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&lmg6202ulyt_opc>;
irq-gpios = <&pio 0 12 GPIO_ACTIVE_LOW>;
interrupt-parent = <&pio>;
/*interrupts = <GIC_SPI 12 IRQ_TYPE_EDGE_FALLING>;*/
};
};

View File

@ -5,8 +5,11 @@
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#define BPP 8 // 8
#define PIX_THRESHOLD 0
@ -31,6 +34,20 @@ static struct of_device_id lmg6202ulyt_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, lmg6202ulyt_dt_ids);
/* IOC Declerations */
//static int device_open(struct inode *, struct file *);
//static int device_release(struct inode *, struct file *);
//static ssize_t device_read(struct file *, char *, size_t, loff_t *);
//static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
//static struct file_operations fops = {
// .read = device_read,
// .write = device_write,
// .open = device_open,
// .release = device_release
//};
/* VIDEO DRIVER Declarations */
static const struct fb_videomode default_mode = {
NULL, 30, 480, 128, /* name, refresh, xres, yres */
39300, /* pixclk */
@ -40,14 +57,208 @@ static const struct fb_videomode default_mode = {
};
struct lmg6202ulyt_priv {
/* SPI dev */
struct spi_device *spi;
struct fb_info info;
/* FB dev */
struct fb_info info;
dma_addr_t fb_phys;
void __iomem *fb_virt;
u32 pseudo_palette[PALETTE_SIZE];
/* IOC */
struct gpio_desc *ioc_irq;
int ioc_irq_number;
struct work_struct ioc_work;
struct gpio_desc *gpio_ioc_cs;
u32 deferred_instructions;
};
/* IOC Code */
#define IOC_DEFERRED_INSTRUCTION_GETKEY (1 << 0)
static irqreturn_t ioc_irq_handler(int irq, void* user_data)
{
struct lmg6202ulyt_priv *priv = user_data;
priv->deferred_instructions |= IOC_DEFERRED_INSTRUCTION_GETKEY;
schedule_work(&priv->ioc_work);
return IRQ_HANDLED;
}
#define SPI_SELECT_IOC(PRIV) {PRIV->spi->chip_select = 1; PRIV->spi->cs_gpio = desc_to_gpio(PRIV->gpio_ioc_cs);}
#define SPI_SELECT_DISPLAY(PRIV) {PRIV->spi->chip_select = 0; PRIV->spi->cs_gpio = -2;}
#define SPI_COMMAND_KEYBOARD_GETKEY 0x20 /* ret: KEY, ... */
/* Bits of the IOC status byte */
#define IOC_STATUS_KEYPRESSES_AVAILABLE (1 << 0)
#define IOC_STATUS_PRINTER_PAUSED (1 << 1)
#define GETKEY_AMOUNT 2
#define SPI_SPEED_READWRITE 10000
#define SPI_SPEED_WRITEONLY 100000
void spi_test_transaction(struct lmg6202ulyt_priv *priv)
{
char tx_buf[256] = "\x01The quick brown fox jumps over the lazy dog.\n"
"abcdefghijklmnopqrstuvwxyz 0123456789\n"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
"!\"#$%&'()*+,-./:;=?_`|\n\n"
;//{0x01, 0x10, 0x20, 0x30};
char rx_buf[256];
int i;
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = strlen(tx_buf),
.speed_hz = SPI_SPEED_WRITEONLY,
};
tx_buf[0] = 0x01;
// for (i =1; i < 120; ++i) {
// tx_buf[i] = (i % 12) + '0';
// if (tx_buf[i] == '9'+1)
// tx_buf[i] = '\r';
// if (tx_buf[i] == '9'+2)
// tx_buf[i] = '\n';
// }
spi_sync_transfer(priv->spi, &t, 1);
printk(KERN_INFO "test done.\n");
}
/* https://linux-kernel-labs.github.io/master/labs/deferred_work.html */
void ioc_defio(struct work_struct *work)
{
#define ioc_status rx_buf[0]
struct lmg6202ulyt_priv *priv;
int i;
char rx_buf[8] = {0, 0, 0, 0}; // Minimum size: GETKEY_AMOUNT + 2
char tx_buf[8] = {0, 0x10, 0x20, 0x30}; // Minimum size: GETKEY_AMOUNT + 2
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 0,
.speed_hz = SPI_SPEED_READWRITE,
};
priv = container_of(work, struct lmg6202ulyt_priv, ioc_work);
//printk(KERN_INFO "lmg: workpriv=%p\n", priv);
//SPI_SELECT_DISPLAY(priv);
priv->spi->chip_select = 1;
priv->spi->cs_gpio = desc_to_gpio(priv->gpio_ioc_cs);
// if (priv->deferred_instructions & IOC_DEFERRED_INSTRUCTION_GETKEY) {
// // Get status + n keys
// tx_buf[0] = SPI_COMMAND_KEYBOARD_GETKEY;
// t.len = GETKEY_AMOUNT + 2;
// if (spi_sync_transfer(priv->spi, &t, 1))
// goto SPI_ERR;
// for (i = 2; (i < GETKEY_AMOUNT + 2) && rx_buf[i]; ++i) {
// printk(KERN_INFO "lmg: Report key: %x\n", rx_buf[i]);
// }
// if (i < GETKEY_AMOUNT + 2) {
// printk(KERN_INFO "lmg: getkey finished.\n");
// ioc_status &= ~IOC_STATUS_KEYPRESSES_AVAILABLE;
// } else {
// printk(KERN_INFO "lmg: Want more keys!!\n");
// }
// }
spi_test_transaction(priv);
if (ioc_status) {
// TODO: Handle non-zero IOC status
// schedule_work(&priv->ioc_work);
}
// struct spi_transfer t = {
// .tx_buf = "HELLO\r\n",
// .len = 7,
// .speed_hz = 1000000,
// };
// if (evil) {
// SPI_SELECT_IOC(priv);
// } else {
// SPI_SELECT_DISPLAY(priv);
// }
// evil = !evil;
// printk(KERN_INFO "cs %d/%d", priv->spi->chip_select, priv->spi->cs_gpio);
// spi_sync_transfer(priv->spi, &t, 1);
// printk(KERN_INFO "lmg: SPI transaction done: %d\n", ret);
return;
SPI_ERR:
printk(KERN_ERR "lmg: IOC SPI transaction failed!\n");
}
static int ioc_probe(struct spi_device *spi,
struct lmg6202ulyt_priv *priv)
{
priv->ioc_irq_number = -1;
printk(KERN_INFO "probe priv=%p", priv);
// priv->ioc_work = devm_kzalloc(&spi->dev, sizeof(struct work_struct),
// GFP_KERNEL);
// if (!priv->ioc_work) {
// dev_err(&spi->dev,
// "IOC defio memory allocation failed\n");
// return -ENOMEM;
// }
INIT_WORK(&priv->ioc_work, ioc_defio);
printk(KERN_INFO "lmg: ioc defio init complete");
priv->gpio_ioc_cs = gpiod_get_index(&spi->dev,
"cs",
1,
GPIOD_OUT_LOW);
priv->ioc_irq = gpiod_get_index(&spi->dev,
"irq",
0,
GPIOD_IN);
if (IS_ERR(priv->ioc_irq)) {
printk(KERN_ERR "lmg: Error obtaining IRQ GPIO");
return -EIO;
}
priv->ioc_irq_number = gpiod_to_irq(priv->ioc_irq);
if (priv->ioc_irq_number < 0) {
printk(KERN_ERR "lmg: Error retrieving IRQ number for GPIO. ret=%d\n", priv->ioc_irq_number);
return -EIO;
}
printk(KERN_INFO "Found IRQ number: %d\n", priv->ioc_irq_number);
if (request_irq(priv->ioc_irq_number, ioc_irq_handler, 0, "ioc", (void*) priv)) {
printk(KERN_ERR "lmg: Can't allocate irq %d\n", priv->ioc_irq_number);
priv->ioc_irq_number = -1;
return -EBUSY;
}
printk(KERN_INFO "IRQ configuration complete\n");
priv->deferred_instructions = 0;
return 0;
}
/* VIDEO DRIVER CODE */
static int lmg6202ulyt_init_var(struct lmg6202ulyt_priv *priv)
{
struct fb_var_screeninfo *var = &priv->info.var;
@ -240,6 +451,7 @@ static int lmg6202ulyt_probe(struct spi_device *spi)
{
struct lmg6202ulyt_priv *priv = NULL;
struct fb_deferred_io *fbdefio = NULL;
int fbsize;
int ret;
@ -258,6 +470,10 @@ static int lmg6202ulyt_probe(struct spi_device *spi)
priv->info.device = &spi->dev;
priv->info.par = priv;
ioc_probe(spi, priv); // TODO: Handle return value
printk(KERN_INFO "lmg6202ulyt: DEBUG: SKIP DISPLAY INIT\n");
return 0;
printk(KERN_INFO "lmg6202ulyt: vmode\n");
@ -346,7 +562,16 @@ static int lmg6202ulyt_remove(struct spi_device *spi)
struct lmg6202ulyt_priv *priv = spi_get_drvdata(spi);
//unregister_framebuffer(&priv->info);
fb_dealloc_cmap(&priv->info.cmap);
printk(KERN_INFO "DEBUG: SKIPPING framebuffer dealloc");
//fb_dealloc_cmap(&priv->info.cmap); // TODO: reenable
if (priv->ioc_irq_number >= 0)
free_irq(priv->ioc_irq_number, priv);
gpiod_put(priv->ioc_irq);
gpiod_put(priv->gpio_ioc_cs);
return 0;
}