Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
5226513971 |
30
display/kernel-driver/device-tree-opi-pc-ioc.dts
Normal file
30
display/kernel-driver/device-tree-opi-pc-ioc.dts
Normal 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>;*/
|
||||||
|
};
|
||||||
|
};
|
@ -5,8 +5,11 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/fb.h>
|
#include <linux/fb.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#define BPP 8 // 8
|
#define BPP 8 // 8
|
||||||
#define PIX_THRESHOLD 0
|
#define PIX_THRESHOLD 0
|
||||||
@ -31,6 +34,20 @@ static struct of_device_id lmg6202ulyt_dt_ids[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, 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 = {
|
static const struct fb_videomode default_mode = {
|
||||||
NULL, 30, 480, 128, /* name, refresh, xres, yres */
|
NULL, 30, 480, 128, /* name, refresh, xres, yres */
|
||||||
39300, /* pixclk */
|
39300, /* pixclk */
|
||||||
@ -40,14 +57,208 @@ static const struct fb_videomode default_mode = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct lmg6202ulyt_priv {
|
struct lmg6202ulyt_priv {
|
||||||
|
/* SPI dev */
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct fb_info info;
|
|
||||||
|
|
||||||
|
/* FB dev */
|
||||||
|
struct fb_info info;
|
||||||
dma_addr_t fb_phys;
|
dma_addr_t fb_phys;
|
||||||
void __iomem *fb_virt;
|
void __iomem *fb_virt;
|
||||||
u32 pseudo_palette[PALETTE_SIZE];
|
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)
|
static int lmg6202ulyt_init_var(struct lmg6202ulyt_priv *priv)
|
||||||
{
|
{
|
||||||
struct fb_var_screeninfo *var = &priv->info.var;
|
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 lmg6202ulyt_priv *priv = NULL;
|
||||||
struct fb_deferred_io *fbdefio = NULL;
|
struct fb_deferred_io *fbdefio = NULL;
|
||||||
|
|
||||||
int fbsize;
|
int fbsize;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -258,6 +470,10 @@ static int lmg6202ulyt_probe(struct spi_device *spi)
|
|||||||
priv->info.device = &spi->dev;
|
priv->info.device = &spi->dev;
|
||||||
priv->info.par = priv;
|
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");
|
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);
|
struct lmg6202ulyt_priv *priv = spi_get_drvdata(spi);
|
||||||
|
|
||||||
//unregister_framebuffer(&priv->info);
|
//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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user