diff --git a/display/kernel-driver/device-tree-opi-pc-ioc.dts b/display/kernel-driver/device-tree-opi-pc-ioc.dts new file mode 100644 index 0000000..0b9617c --- /dev/null +++ b/display/kernel-driver/device-tree-opi-pc-ioc.dts @@ -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 = ;*/ + }; +}; diff --git a/display/kernel-driver/device-tree-opi-pc.dtb b/display/kernel-driver/device-tree-opi-pc.dts similarity index 100% rename from display/kernel-driver/device-tree-opi-pc.dtb rename to display/kernel-driver/device-tree-opi-pc.dts diff --git a/display/kernel-driver/lmg6202ulyt.c b/display/kernel-driver/lmg6202ulyt.c index dfc5453..ea67be2 100644 --- a/display/kernel-driver/lmg6202ulyt.c +++ b/display/kernel-driver/lmg6202ulyt.c @@ -5,8 +5,11 @@ #include #include #include +#include #include #include +#include +#include #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; }