| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/i2c.h>
- #include <linux/uaccess.h>
- #include <linux/miscdevice.h>
- #include <linux/pci.h>
- #include <linux/mutex.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #define DRIVER_NAME "lcd2002"
- #define LCD_I2C_ADDR_DEFAULT 0x3E
- #define LCD_CONTROL_BYTE 0x00
- #define LCD_DATA_BYTE 0x40
- #define LCD_CLEAR_DISPLAY 0x01
- #define LCD_RETURN_HOME 0x02
- #define LCD_ENTRY_MODE_SET 0x04
- #define LCD_DISPLAY_CONTROL 0x08
- #define LCD_CURSOR_SHIFT 0x10
- #define LCD_FUNCTION_SET 0x20
- #define LCD_SET_CGRAM_ADDR 0x40
- #define LCD_SET_DDRAM_ADDR 0x80
- /* Entry Mode */
- #define LCD_ENTRY_RIGHT 0x00
- #define LCD_ENTRY_LEFT 0x02
- /* Display Control */
- #define LCD_DISPLAY_ON 0x04
- #define LCD_DISPLAY_OFF 0x00
- #define LCD_CURSOR_ON 0x02
- #define LCD_CURSOR_OFF 0x00
- #define LCD_BLINK_ON 0x01
- #define LCD_BLINK_OFF 0x00
- #define LCD_LINE1_ADDR 0x00
- #define LCD_LINE2_ADDR 0x40
- #define LCD_COLS 20
- #define LCD_ROWS 2
- /* IOCTL */
- #define LCD_IOCTL_MAGIC 'L'
- #define LCD_IOCTL_CLEAR _IO(LCD_IOCTL_MAGIC, 0)
- #define LCD_IOCTL_HOME _IO(LCD_IOCTL_MAGIC, 1)
- #define LCD_IOCTL_SET_CURSOR _IOW(LCD_IOCTL_MAGIC, 2, struct lcd_pos)
- #define LCD_IOCTL_DISPLAY_CTRL _IOW(LCD_IOCTL_MAGIC, 3, unsigned char[3])
- struct lcd_pos {
- unsigned char col;
- unsigned char row;
- };
- struct lcd2002_dev {
- struct i2c_adapter *adapter;
- struct mutex lock;
- unsigned char addr;
- };
- static struct lcd2002_dev *lcd_dev = NULL;
- static char *pci_addr = "0000:00:15.0";
- module_param(pci_addr, charp, 0444);
- MODULE_PARM_DESC(pci_addr, "PCI address (e.g., 0000:00:15.0)");
- static char *adapter_name = NULL;
- module_param(adapter_name, charp, 0444);
- MODULE_PARM_DESC(adapter_name, "I2C adapter name (e.g., Synopsys DesignWare)");
- static int i2c_bus = -1;
- module_param(i2c_bus, int, 0444);
- MODULE_PARM_DESC(i2c_bus, "I2C bus number (e.g., 1 for /dev/i2c-1)");
- static unsigned char i2c_address = LCD_I2C_ADDR_DEFAULT;
- module_param(i2c_address, byte, 0444);
- MODULE_PARM_DESC(i2c_address, "LCD I2C address (default: 0x3E)");
- static int debug = 1;
- module_param(debug, int, 0644);
- MODULE_PARM_DESC(debug, "Debug level (0=none, 1=normal, 2=verbose)");
- static inline void lcd_delay_ms(unsigned int ms)
- {
- msleep(ms);
- }
- static int lcd_i2c_write(unsigned char *buf, int len)
- {
- struct i2c_msg msg;
- int ret;
- if (!lcd_dev || !lcd_dev->adapter)
- return -ENODEV;
- msg.addr = lcd_dev->addr;
- msg.flags = 0;
- msg.len = len;
- msg.buf = buf;
- ret = i2c_transfer(lcd_dev->adapter, &msg, 1);
- if (ret != 1) {
- dev_err(&lcd_dev->adapter->dev, "I2C write failed: %d\n", ret);
- return ret < 0 ? ret : -EIO;
- }
- return 0;
- }
- static int lcd_send_command(unsigned char cmd)
- {
- unsigned char buf[2];
- int ret;
- buf[0] = LCD_CONTROL_BYTE;
- buf[1] = cmd;
- mutex_lock(&lcd_dev->lock);
- ret = lcd_i2c_write(buf, 2);
- mutex_unlock(&lcd_dev->lock);
- if (ret == 0)
- lcd_delay_ms(2);
- return ret;
- }
- static int lcd_send_data(unsigned char data)
- {
- unsigned char buf[2];
- int ret;
- buf[0] = LCD_DATA_BYTE;
- buf[1] = data;
- mutex_lock(&lcd_dev->lock);
- ret = lcd_i2c_write(buf, 2);
- mutex_unlock(&lcd_dev->lock);
- if (ret == 0)
- lcd_delay_ms(1);
- return ret;
- }
- static int lcd_init_display(void)
- {
- int ret;
- lcd_delay_ms(50);
- ret = lcd_send_command(LCD_FUNCTION_SET | 0x08);
- if (ret < 0) return ret;
- lcd_delay_ms(5);
- ret = lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF);
- if (ret < 0) return ret;
- ret = lcd_send_command(LCD_CLEAR_DISPLAY);
- if (ret < 0) return ret;
- lcd_delay_ms(3);
- ret = lcd_send_command(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT);
-
- return ret;
- }
- static int lcd_set_cursor(unsigned char col, unsigned char row)
- {
- unsigned char addr;
- if (col >= LCD_COLS || row >= LCD_ROWS)
- return -EINVAL;
- addr = (row == 0) ? (LCD_LINE1_ADDR + col) : (LCD_LINE2_ADDR + col);
- return lcd_send_command(LCD_SET_DDRAM_ADDR | addr);
- }
- static int lcd_clear(void)
- {
- int ret = lcd_send_command(LCD_CLEAR_DISPLAY);
- if (ret == 0) lcd_delay_ms(2);
- return ret;
- }
- static int lcd_display_control(unsigned char display, unsigned char cursor, unsigned char blink)
- {
- return lcd_send_command(LCD_DISPLAY_CONTROL | display | cursor | blink);
- }
- static int lcd_write_string(const char *str, size_t len)
- {
- size_t i;
- int ret = 0;
- unsigned char row = 0, col = 0;
- for (i = 0; i < len; i++) {
- char c = str[i];
- if (c == '\n') {
- row = (row + 1) % LCD_ROWS;
- col = 0;
- ret = lcd_set_cursor(col, row);
- } else if (c == '\r') {
- col = 0;
- ret = lcd_set_cursor(col, row);
- } else if (c == '\f') {
- ret = lcd_clear();
- row = col = 0;
- } else {
- if (col >= LCD_COLS) {
- row = (row + 1) % LCD_ROWS;
- col = 0;
- ret = lcd_set_cursor(col, row);
- if (ret < 0) break;
- }
- ret = lcd_send_data(c);
- col++;
- }
- if (ret < 0) break;
- }
- return ret;
- }
- static void print_adapter_info(struct i2c_adapter *adap, int nr)
- {
- struct device *parent;
- char pci_str[64] = "N/A";
-
- if (!debug) return;
-
- pr_info(" [%d] %s (class: 0x%lx)\n", nr, adap->name, adap->class);
-
- parent = adap->dev.parent;
- while (parent) {
- if (parent->bus == &pci_bus_type) {
- struct pci_dev *pdev = to_pci_dev(parent);
- snprintf(pci_str, sizeof(pci_str), "%04x:%02x:%02x.%x",
- pci_domain_nr(pdev->bus),
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
- pr_info(" -> PCI: %s [%s]\n", pci_str, dev_name(parent));
- break;
- } else {
- pr_info(" -> %s [%s]\n", dev_name(parent), parent->bus ? parent->bus->name : "no-bus");
- }
- parent = parent->parent;
- }
- }
- static struct i2c_adapter *find_by_pci(const char *target_pci)
- {
- struct i2c_adapter *adapter;
- int nr = 0;
- bool found = false;
-
- pr_info("Searching for PCI %s...\n", target_pci);
-
- while ((adapter = i2c_get_adapter(nr))) {
- struct device *parent = adapter->dev.parent;
- char pci_buf[32] = "";
-
- if (debug >= 2)
- print_adapter_info(adapter, nr);
-
- while (parent && !found) {
- if (parent->bus == &pci_bus_type) {
- struct pci_dev *pdev = to_pci_dev(parent);
- snprintf(pci_buf, sizeof(pci_buf), "%04x:%02x:%02x.%x",
- pci_domain_nr(pdev->bus),
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
-
- if (strcmp(pci_buf, target_pci) == 0) {
- pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
- found = true;
- break;
- }
- break;
- }
- parent = parent->parent;
- }
-
- if (found)
- return adapter;
-
- i2c_put_adapter(adapter);
- nr++;
- }
-
- return NULL;
- }
- static struct i2c_adapter *find_by_name(const char *name)
- {
- struct i2c_adapter *adapter;
- int nr = 0;
-
- pr_info("Searching for adapter name '%s'...\n", name);
-
- while ((adapter = i2c_get_adapter(nr))) {
- if (debug >= 2)
- print_adapter_info(adapter, nr);
-
- if (strstr(adapter->name, name)) {
- pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
- return adapter;
- }
- i2c_put_adapter(adapter);
- nr++;
- }
-
- return NULL;
- }
- static struct i2c_adapter *find_by_bus(int bus)
- {
- pr_info("Trying i2c bus %d...\n", bus);
- return i2c_get_adapter(bus);
- }
- static struct i2c_adapter *find_adapter(void)
- {
- struct i2c_adapter *adapter = NULL;
-
- if (i2c_bus >= 0) {
- adapter = find_by_bus(i2c_bus);
- if (adapter) goto done;
- pr_warn("Specified i2c_bus %d not found\n", i2c_bus);
- }
-
- if (pci_addr && strlen(pci_addr) > 0) {
- adapter = find_by_pci(pci_addr);
- if (adapter) goto done;
- pr_warn("PCI address %s not found\n", pci_addr);
- }
-
- if (adapter_name && strlen(adapter_name) > 0) {
- adapter = find_by_name(adapter_name);
- if (adapter) goto done;
- pr_warn("Adapter name '%s' not found\n", adapter_name);
- }
-
- pr_info("Trying auto-detect (Synopsys DesignWare)...\n");
- adapter = find_by_name("Synopsys DesignWare");
- if (adapter) goto done;
-
- pr_info("Trying fallback i2c-1...\n");
- adapter = find_by_bus(1);
-
- done:
- return adapter;
- }
- static int lcd_open(struct inode *inode, struct file *file)
- {
- return lcd_dev ? 0 : -ENODEV;
- }
- static int lcd_release(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static ssize_t lcd_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
- {
- char *kbuf;
- int ret;
-
- if (count == 0) return 0;
- if (count > 256) count = 256;
-
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) return -ENOMEM;
-
- if (copy_from_user(kbuf, buf, count)) {
- kfree(kbuf);
- return -EFAULT;
- }
- kbuf[count] = '\0';
-
- ret = lcd_write_string(kbuf, count);
- kfree(kbuf);
-
- return ret < 0 ? ret : count;
- }
- static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- int ret = 0;
-
- switch (cmd) {
- case LCD_IOCTL_CLEAR:
- ret = lcd_clear();
- break;
- case LCD_IOCTL_HOME:
- ret = lcd_send_command(LCD_RETURN_HOME);
- if (ret == 0) lcd_delay_ms(2);
- break;
- case LCD_IOCTL_SET_CURSOR: {
- struct lcd_pos pos;
- if (copy_from_user(&pos, (void __user *)arg, sizeof(pos)))
- return -EFAULT;
- ret = lcd_set_cursor(pos.col, pos.row);
- break;
- }
- case LCD_IOCTL_DISPLAY_CTRL: {
- unsigned char ctrl[3];
- if (copy_from_user(ctrl, (void __user *)arg, sizeof(ctrl)))
- return -EFAULT;
- ret = lcd_display_control(ctrl[0], ctrl[1], ctrl[2]);
- break;
- }
- default:
- ret = -EINVAL;
- }
- return ret;
- }
- static const struct file_operations lcd_fops = {
- .owner = THIS_MODULE,
- .open = lcd_open,
- .release = lcd_release,
- .write = lcd_write,
- .unlocked_ioctl = lcd_ioctl,
- .compat_ioctl = lcd_ioctl,
- };
- static struct miscdevice lcd_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lcd2002",
- .fops = &lcd_fops,
- .mode = 0666,
- };
- int lcd2x20_init(void)
- {
- int ret;
-
- pr_info("LCD2002 driver loading (PCI:%s, Bus:%d, Addr:0x%02X)...\n",
- pci_addr ? pci_addr : "none", i2c_bus, i2c_address);
-
- lcd_dev = kzalloc(sizeof(*lcd_dev), GFP_KERNEL);
- if (!lcd_dev)
- return -ENOMEM;
-
- mutex_init(&lcd_dev->lock);
- lcd_dev->addr = i2c_address;
-
- lcd_dev->adapter = find_adapter();
- if (!lcd_dev->adapter) {
- pr_err("Failed to find any suitable I2C adapter!\n");
- pr_err("Available adapters (from i2cdetect -l):\n");
- {
- int nr = 0;
- struct i2c_adapter *a;
- while ((a = i2c_get_adapter(nr++))) {
- pr_err(" i2c-%d: %s\n", nr-1, a->name);
- i2c_put_adapter(a);
- }
- }
- ret = -ENODEV;
- goto err_free;
- }
-
- pr_info("Using I2C adapter: %s\n", lcd_dev->adapter->name);
-
- ret = lcd_init_display();
- if (ret < 0) {
- pr_err("LCD init failed: %d\n", ret);
- goto err_put;
- }
-
- ret = misc_register(&lcd_miscdev);
- if (ret < 0) {
- pr_err("Misc register failed: %d\n", ret);
- goto err_put;
- }
-
- pr_info("LCD2002 ready at /dev/%s\n", lcd_miscdev.name);
-
- lcd_clear();
- lcd_write_string("LCD2002 Driver OK", 17);
- lcd_set_cursor(0, 1);
- lcd_write_string("Intel ADL-N97", 13);
-
- return 0;
- err_put:
- i2c_put_adapter(lcd_dev->adapter);
- err_free:
- kfree(lcd_dev);
- lcd_dev = NULL;
- return ret;
- }
- void lcd2x20_exit(void)
- {
- if (!lcd_dev) return;
-
- misc_deregister(&lcd_miscdev);
- lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF);
-
- if (lcd_dev->adapter)
- i2c_put_adapter(lcd_dev->adapter);
-
- kfree(lcd_dev);
- lcd_dev = NULL;
- pr_info("LCD2002 driver unloaded\n");
- }
|